本人写了一个程序,使用了一个开源的ChartCtrl(http://www.codeproject.com/KB/miscctrl/High-speedCharting.aspx),控件在刷新时要使用GetTextExtent来获取控件上字符串的尺寸。
在界面视图A上放置CChartCtrl控件,一个工作线程去定时取数据,然后调用界面视图A的方法将数据数据在CChartCtrl控件上界面出来,程序在运行的时候有时出现f:\rtm\vctools\vc7libs\ship\atlmfc\include\afxwin1.inl,即GetTextExtent内部调用GetExtentPoint32()返回FALSE导致VERIFY失败而弹出的对话框,如果DEBUG模时,忽略掉该问题了,程序又可以正常运行了,但过一段时间又出这样的问题。仔细分析了CChartCtrl的代码,m_hDC绝对是有效的,而且在运行过程中没有改过CDC对象。并且不存在多线程访问界面视图A。
出现问题后调用GetLastError()都返回0,即正常。好奇怪的问题了,搞了一周了还没有找到解决方案,先谢谢各位的帮助了。

解决方案 »

  1.   

    是的,在GetTextExtent()首先会判断hAttribDC 不等于NULL,然后才会调用GetExtentPoint32()
      

  2.   

    GetTextExtent()是如何调用的,不要在工作线程中调用该函数。
      

  3.   

    在界面视图A上放置CChartCtrl控件,一个工作线程去定时取数据,然后调用界面视图A的方法将数据数据在CChartCtrl控件上界面出来,程序在运行的时候有时出现f:\rtm\vctools\vc7libs\ship\atlmfc\include\afxwin1.inl,即GetTextExtent内部调用GetExtentPoint32()返回FALSE导致VERIFY失败而弹出的对话框,如果DEBUG模时,忽略掉该问题了,程序又可以正常运行了,但过一段时间又出这样的问题。 仔细分析了CChartCtrl的代码,m_hDC绝对是有效的,而且在运行过程中没有改过CDC对象。并且不存在多线程访问界面视图A。明显是多线程引起的问题,怎么还说不存在 多线程访问界面视图A应该工作线程取得数据后,发消息告诉视图A,A读数据刷新CChartCtrl控件
      

  4.   

    有空自己找点时间来分析一下MFC里面的机制,希望能找到答案。
    TO Conry:
    即使是多线程但每次数据刷新界面都是由工作线程来驱动,其他情况下,不会得画控件的啊。现在只能通过减少重画控件的次数来避免这个问题。太奇怪了,如果VERIFY是由于CDC句柄造成的话,继续运行应该就会不断重复出现才对啊。
      

  5.   

    所有的操作凡在DrawTitle的地方,Purify 都是报的这个UMR错:    [W] UMR: Uninitialized memory read in ExtTextOutA {1 occurrence}
            Reading 1 byte from 0x031dc480 (1 byte at 0x031dc480 uninitialized)
            Address 0x031dc480 is argument #6 of ExtTextOutA
            Address 0x031dc480 is 56 bytes into a 63 byte block at 0x031dc448
            Address 0x031dc480 points to a HeapAlloc'd block in heap 0x02450000
            Thread ID: 0xde4
            Error location
            ExtTextOutA    [GDI32.dll]
            CDC::ExtTextOutA(int,int,UINT,tagRECT const*,CStringT <char,StrTraitMFC <char,ChTraitsCRT <char>::ATL>>::ATL const&,int *) [afxwin1.inl:609]
            CChartTitle::Draw(CDC *) [charttitle.cpp:115]
            CChartCtrl::DrawChart(CDC *,CRect) [chartctrl.cpp:468]
            CChartCtrl::RefreshCtrl(void) [chartctrl.cpp:436]
            CChartCtrl::EnableRefresh(bool) [chartctrl.cpp:376]
            CSmsFlowFormView::Init(void) [smsflowformview.cpp:125]
            CSmsFlowFormView::OnInitialUpdate(void) [smsflowformview.cpp:67]
            CWnd::OnWndMsg(UINT,UINT,long,long *) [wincore.cpp:2027]
            CWnd::WindowProc(UINT,UINT,long) [wincore.cpp:1741]
        Allocation location
            HeapAlloc      [KERNEL32.dll]
            heap_alloc_base [malloc.c:105]
            heap_alloc_dbg [dbgheap.c:409]
            nh_malloc_dbg  [dbgheap.c:266]
            malloc        [dbgheap.c:152]
            CAfxStringMgr::Allocate(int,int) [strcore.cpp:141]
            CSmsFlowFormView::Init(void) [smsflowformview.cpp:125]
            CSmsFlowFormView::OnInitialUpdate(void) [smsflowformview.cpp:67]
            CWnd::OnWndMsg(UINT,UINT,long,long *) [wincore.cpp:2027]
            CWnd::WindowProc(UINT,UINT,long) [wincore.cpp:1741]
        Allocation location
            HeapAlloc      [KERNEL32.dll]
            heap_alloc_base [malloc.c:105]
            heap_alloc_dbg [dbgheap.c:409]
            nh_malloc_dbg  [dbgheap.c:266]
            malloc        [dbgheap.c:152]
            CAfxStringMgr::Allocate(int,int) [strcore.cpp:141]
            ATL::CSimpleStringT <char,0>::Fork(int) [atlsimpstr.h:794]
            ATL::CSimpleStringT <char,0>::PrepareWrite2(int) [atlsimpstr.h:835]
            ATL::CSimpleStringT <char,0>::PrepareWrite(int) [atlsimpstr.h:821]
            ATL::CSimpleStringT <char,0>::GetBuffer(int) [atlsimpstr.h:528]
      

  6.   

    [W] UMR: Uninitialized memory read in GetTextExtentPoint32A {1 occurrence}
            Reading 1 byte from 0x031dc348 (1 byte at 0x031dc348 uninitialized)
            Address 0x031dc348 is argument #2 of GetTextExtentPoint32A
            Address 0x031dc348 is 56 bytes into a 63 byte block at 0x031dc310
            Address 0x031dc348 points to a HeapAlloc'd block in heap 0x02450000
            Thread ID: 0xde4
            Error location
                GetTextExtentPoint32A [GDI32.dll]
                CDC::GetTextExtent(CStringT <char,StrTraitMFC <char,ChTraitsCRT <char>::ATL>>::ATL const&)const [afxwin1.inl:666]
                        {
                            ASSERT(m_hAttribDC != NULL);
                            SIZE size;
                =>        VERIFY(::GetTextExtentPoint32(m_hAttribDC, str, (int)str.GetLength(), &size));
                            return size;
                        }
                   
                CChartTitle::GetSize(CDC *) [charttitle.cpp:154]
    [W] UMR: Uninitialized memory read in GetTextExtentPoint32A {1 occurrence}
                   
                CChartTitle::GetSize(CDC *) [charttitle.cpp:154]
                CChartCtrl::DrawChart(CDC *,CRect) [chartctrl.cpp:461]
                CChartCtrl::RefreshCtrl(void) [chartctrl.cpp:436]
                CChartCtrl::EnableRefresh(bool) [chartctrl.cpp:376]
                CSmsFlowFormView::Init(void) [smsflowformview.cpp:125]
                CSmsFlowFormView::OnInitialUpdate(void) [smsflowformview.cpp:67]
                CWnd::OnWndMsg(UINT,UINT,long,long *) [wincore.cpp:2027]
                CWnd::WindowProc(UINT,UINT,long) [wincore.cpp:1741] 
      

  7.   

    MFC很多类和方法都是非线程安全的.
      

  8.   

    在界面视图A上放置CChartCtrl控件,一个工作线程去定时取数据,然后调用界面视图A的方法将数据数据在CChartCtrl控件上界面出来...
    ---------------------
    你线程中将数据数据在CChartCtrl控件上显示出来,会调用GetDC,但是别忘了界面也是会刷新的,也需要调用GetDC,这就是两个线程在抢DC,当然可能会失效了。
      

  9.   

    TO ringphone,最大的可能性就是这样,请教一下,有没有好办法避免出现界面和后台刷新时候出现竞争呢?我现在的做法是减少工作线程刷新的次数(减少无用的刷新),不知道界面是何时刷新的
      

  10.   

    先谢谢大家。
    应该找到问题了,每次工作线程调用添加点的函数AddPoint(),AddPoint()返回前都要调用CWnd::Invalidate()让ChartCtrl客户区无效,并产生一条WM_PAINT消息到窗口的消息队列中,未等WM_PAINT消息处理之前,Invalid()就返回了,未重画前,应该客户区的CDC对象也可能无效。
    但如果工作线程在此时来刷新窗口时,使用到的CChartCtrl的客户区的CDC对象可能无效(在调用了Invalidate()之后、WM_PAINT消息处理之前),所以导致了GetTextPoint32()函数失败。
      

  11.   

    您好,我也出现了同描述的问题一样,我使用的是多线程里面调用的GetTextExtent()函数,然后会偶然跳到
    VERIFY(::GetTextExtentPoint32(m_hAttribDC, str, (int)str.GetLength(), &size));
    继续的话,会正常运行,请问你这种问题如何解决的?请指教。
      

  12.   

    我想我找到方法了,在codeproject 里有解决方法:
    that is valid for all UI related stuff: never access anything UI related from outside the main thread.You could instead put all your data in a buffer and send a message to the UI thread to signal that data is ready. You'll need to protect the access to the buffer properly.
     
    There is a very good article about threading here[^], it is a very good read for anybody working with threads.
    BTW, why don't you send each point of data to the control ? This way you could see the data "live" (exactly like an oscilloscope) ?