header image
 

Let there be HFONT

After some preliminary refactoring of PuTTY Recorder’s code I started it and was greeted with an unresponsive window. It wasn’t completely locked, just very, very slow at updating. First thought – some kind of rampant recursion in OnPaint handler, but that would lock the window up for good. So I launched the profiler… and found nothing unusual. Dialog procedure was on top of inclusive samples of course, and then just updating and painting – well duh, I knew that already. Tried to profile with instrumentation instead of sampling in hope of getting better accuracy, but that also got me nowhere. Huh. I stepped through the program initialization and everything seemed to be in order. Oh well, time for the best debugging method – DebugPrint()! I added print statements on top of the most suspicious functions and restarted the application.

PuttyRecDlg::OnPaint
TerminalFrame::Draw
PuttyRecDlg::OnUpdate
PuttyRecDlg::OnPaint
TerminalFrame::Draw
PuttyRecDlg::OnUpdate
PuttyRecDlg::OnPaint
TerminalFrame::Draw

Uh-oh. As I watched the output, I realized that each Draw call took seconds to complete. What the hell? It’s just some text rendering and one bitmap blit. It worked perfectly before. The only major change was that I added the ability for user to select font used for terminal rendering.

void TerminalFrame::Draw(HDC hdc, UINT startx, UINT starty, UINT char_width, UINT char_height) const
{
    if (m_font == 0)
        THROW("No font selected");

    SelectObject(hdc, m_font);

Font wasn’t null since I wasn’t getting exceptions. Checked manually – yeah, SelectObject() call was successful. Then I realized that despite font being seemingly OK, no text was appearing in the window. What gives? I ran the program again, waited agonizingly until it stopped updating and selected some font manually. Lo and behold, that seemed to fix everything – window became responsive and text was visible. Let’s see where the font is being created for the first time then.

void TerminalFrame::SetFont(const LOGFONT* logfont)
{
    if (m_font)
    {
        DeleteObject(m_font);
        m_font = 0;
    }
    m_font = CreateFontIndirect(logfont);
    if (!m_font)
        THROW("CreateFont failed");
}

I put a breakpoint there and restart. What do you know, logfont is uninitialized. Apparently I forgot to set it to something sensible. But wait a second. If logfont is garbage, how does CreateFontIndirect() work? It returns something and is NOT apparently failing. Just when you use the resulting font, text operations are excruciatingly slow (and don’t do much).

So, TLDR: You can create a font from nothing, but don’t expect it to work.

~ by omeg on October 25, 2011.

C/C++, code, troubleshooting, winapi

Leave a Reply