tags:

views:

157

answers:

1

I'm working on a multi-threaded win32 MFC application. We are rendering a map and displaying it in a pane in the user interface along with custom-rendered objects on top. It's slow to render (~800 ms), which is happening on the User Interface thread.

I'm trying to move the rendering onto its own thread so that the menus still remain snappy while the other rendering can still run in the background. The Draw thread will render continually using its own CDC. The UI thread will call a redraw function, which locks the mutex, and takes the last snapshot of the CBitmap and draws it using the UI's CDC. Every location where the Draw thread's CDC is used, is locked by the mutex.

What I'm seeing is the thread creating a new CBitmap via CreatCompatibleBitmap, and then trying to select the new CBitmap object into the Draw thread's CDC.

this->m_canvas.CreateCompatibleDC(&compatibleDC);
this->m_bitmap = new CBitmap();
this->m_bitmap->CreateCompatibleBitmap(&compatibleDC, m_width, m_height);

m_oldBitmap = this->m_canvas.SelectObject(m_bitmap);

At this point, there is a debug ASSERT failure in CGdiObject::FromHandle().

CGdiObject* PASCAL CGdiObject::FromHandle(HGDIOBJ h)
{
    CHandleMap* pMap = afxMapHGDIOBJ(TRUE); //create map if not exist
    ASSERT(pMap != NULL);
    CGdiObject* pObject = (CGdiObject*)pMap->FromHandle(h);
    ASSERT(pObject == NULL || pObject->m_hObject == h);
    return pObject;
}

The second ASSERT is failing because the m_hObject does not match the handle passed in. Basically, MFC is taking the handle, and doing a lookup to get a CBitmap object which somehow doesn't match the CBitmap that was just created.

Does this sound familiar to anyone? What could be happening to cause the FromHandle method to return the wrong object? Is there a fundamental flaw with the way I create a CDC for the Draw thread, and then re-use it over and over? Are there any approaches I can take to help debug/fix this problem?

+1  A: 

Golden. The mapping between handles and objects are in thread-local storage.

In a multi-threaded environment because windows are owned by threads, MFC keeps the temporary and permanent window handle map in thread local storage. The same is true for other handle maps like those for GDI objects and device contexts. Keeping the window handle maps in thread local storage ensures protection against simultaneous access by several threads.

So basically, store the handle, then create a CBitmap from the handle in order to manipulate them between threads.

My mistake was in the UI thread creating my CBitmap, and then accessing the CBitmap object from both threads.

Kieveli
Heh. Made exactly the same mistake moving draw code into its own thread for exactly the same reasons. Got some good answers and references asking a similar question which may be of value; http://stackoverflow.com/questions/2287114/thread-type-for-background-drawing-to-a-bitmap-in-mfc
Shane MacLaughlin