使用CDC::GetTextExtent出现了问题的奇怪问题,m_hDC 是有效的,但GetTextExtent内部调用GetExtentPoint32有时失败

songpingbo 2008-10-12 08:13:29
本人写了一个程序,使用了一个开源的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,即正常。

好奇怪的问题了,搞了一周了还没有找到解决方案,先谢谢各位的帮助了。
...全文
1187 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
Gordon_Freeman 2012-10-22
  • 打赏
  • 举报
回复
遇到同样的问题,感谢楼上的各位对我的帮助!
dingfc 2012-07-20
  • 打赏
  • 举报
回复
我想我找到方法了,在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) ?
ANNE 2012-04-09
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 的回复:]
先谢谢大家。
应该找到问题了,每次工作线程调用添加点的函数AddPoint(),AddPoint()返回前都要调用CWnd::Invalidate()让ChartCtrl客户区无效,并产生一条WM_PAINT消息到窗口的消息队列中,未等WM_PAINT消息处理之前,Invalid()就返回了,未重画前,应该客户区的CDC对象也可能无效。
但如果工作线程在此时来刷新窗口时,使用到的CChartC……
[/Quote]
您好,我也出现了同描述的问题一样,我使用的是多线程里面调用的GetTextExtent()函数,然后会偶然跳到
VERIFY(::GetTextExtentPoint32(m_hAttribDC, str, (int)str.GetLength(), &size));
继续的话,会正常运行,请问你这种问题如何解决的?请指教。
songpingbo 2008-10-23
  • 打赏
  • 举报
回复
TO ringphone,最大的可能性就是这样,请教一下,有没有好办法避免出现界面和后台刷新时候出现竞争呢?我现在的做法是减少工作线程刷新的次数(减少无用的刷新),不知道界面是何时刷新的
ringphone 2008-10-23
  • 打赏
  • 举报
回复
在界面视图A上放置CChartCtrl控件,一个工作线程去定时取数据,然后调用界面视图A的方法将数据数据在CChartCtrl控件上界面出来...
---------------------
你线程中将数据数据在CChartCtrl控件上显示出来,会调用GetDC,但是别忘了界面也是会刷新的,也需要调用GetDC,这就是两个线程在抢DC,当然可能会失效了。
songpingbo 2008-10-23
  • 打赏
  • 举报
回复
先谢谢大家。
应该找到问题了,每次工作线程调用添加点的函数AddPoint(),AddPoint()返回前都要调用CWnd::Invalidate()让ChartCtrl客户区无效,并产生一条WM_PAINT消息到窗口的消息队列中,未等WM_PAINT消息处理之前,Invalid()就返回了,未重画前,应该客户区的CDC对象也可能无效。
但如果工作线程在此时来刷新窗口时,使用到的CChartCtrl的客户区的CDC对象可能无效(在调用了Invalidate()之后、WM_PAINT消息处理之前),所以导致了GetTextPoint32()函数失败。
raymonzhao 2008-10-22
  • 打赏
  • 举报
回复
MFC很多类和方法都是非线程安全的.
songpingbo 2008-10-22
  • 打赏
  • 举报
回复
[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]
songpingbo 2008-10-22
  • 打赏
  • 举报
回复
所有的操作凡在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]
songpingbo 2008-10-20
  • 打赏
  • 举报
回复
有空自己找点时间来分析一下MFC里面的机制,希望能找到答案。
TO Conry:
即使是多线程但每次数据刷新界面都是由工作线程来驱动,其他情况下,不会得画控件的啊。

现在只能通过减少重画控件的次数来避免这个问题。太奇怪了,如果VERIFY是由于CDC句柄造成的话,继续运行应该就会不断重复出现才对啊。
zhoujianhei 2008-10-13
  • 打赏
  • 举报
回复
GetTextExtent()是如何调用的,不要在工作线程中调用该函数。
songpingbo 2008-10-13
  • 打赏
  • 举报
回复
是的,在GetTextExtent()首先会判断hAttribDC 不等于NULL,然后才会调用GetExtentPoint32()
Conry 2008-10-13
  • 打赏
  • 举报
回复 1
在界面视图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控件
zhoujianhei 2008-10-12
  • 打赏
  • 举报
回复
m_hAttribDC必须是有效的
songpingbo 2008-10-12
  • 打赏
  • 举报
回复
对了,程序是用VC2005开发

15,979

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 界面
社区管理员
  • 界面
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧