MFC的CRichEdit控件使用问题

秦风楚月汉宫阙 2021-01-08 03:09:32
最近在使用MFC开发软件的时候遇到了一个非常困惑的问题。软件用到了CRichEdit控件,用于显示彩色log,我也成功得使用了该控件。
该软件在有些同事的电脑上能够正常运行,也能正常显示彩色字体,但在个别同事的电脑上无法正常运行。而且我们用的电脑系统都是win10的。开发环境也都一样。软件在没有开发环境的电脑上也能正常运行。我用得开发环境是Visual Studio 2008.
RichEdit该加的库我也加载了。软件log区显示一部分内容后就崩了,通过断点查看,是在RichEdit的函数GetWindowTextW()函数出问题。提示访问冲突。但之前显示过的内容肯定也调用过这个函数。那时也没出问题。真不知道是啥原因造成这种现象。
...全文
164 点赞 收藏 14
写回复
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
谢谢,大家的建议,花了两天的时间终于找到到了问题的原因:我的工程界面是DLL库工程,在使用钩子函数的时候,出现了很奇怪的现象:我的钩函数是用来捕获回车,结果钩函数在捕获回车发消息的时候,在有的电脑上是发一次,在有些电脑上发2次,而且电脑系统都是一样的。发送1次的那个把键按下和抬起当成一个信号发送,发送2次 的那个把键按下和键抬起当成两个消息发送,结果处理完前一个消息后,处理第二个消息的时候找不到发消息的对象了。出现这种情况让我很纳闷,还好通过栈函数的调用顺序查询到了问题的原因。不知道有没有遇到和 我这一样的情况
回复
全部改成CRichEdit的成员函数还是不行,把子线程中的对RichEdit控件的函数全部去掉还是不行
回复
RichEdit是在子线程中用到了,但我换成RichEdit的成员函数还是有问题
回复
就是这情况,反正软件不让用,怕出问题
回复
mmcanyu 01-09
一般开发的电脑上运行都没问题,别的电脑上运行要看运气,有时可以,有时崩溃。
回复
引用 7 楼 mmcanyu 的回复:
应该还是线程操作的问题。
打开RichEdit的源文件afxcmn.inl看一下,我贴一部分上来。

_AFXCMN_INLINE BOOL CRichEditCtrl::CanUndo() const
{ ASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_CANUNDO, 0, 0); }
_AFXCMN_INLINE BOOL CRichEditCtrl::CanRedo() const
{ ASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_CANREDO, 0, 0); }
_AFXCMN_INLINE UNDONAMEID CRichEditCtrl::GetUndoName() const
{ ASSERT(::IsWindow(m_hWnd)); return (UNDONAMEID) ::SendMessage(m_hWnd, EM_GETUNDONAME, 0, 0); }
_AFXCMN_INLINE UNDONAMEID CRichEditCtrl::GetRedoName() const
{ ASSERT(::IsWindow(m_hWnd)); return (UNDONAMEID) ::SendMessage(m_hWnd, EM_GETREDONAME, 0, 0); }
_AFXCMN_INLINE int CRichEditCtrl::GetLineCount() const
{ ASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_GETLINECOUNT, 0, 0); }
_AFXCMN_INLINE BOOL CRichEditCtrl::GetModify() const
{ ASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_GETMODIFY, 0, 0); }
_AFXCMN_INLINE void CRichEditCtrl::SetModify(BOOL bModified /* = TRUE */)
{ ASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETMODIFY, bModified, 0);}
_AFXCMN_INLINE BOOL CRichEditCtrl::SetTextMode(UINT fMode)
{ ASSERT(::IsWindow(m_hWnd)); return (BOOL) ::SendMessage(m_hWnd, EM_SETTEXTMODE, (WPARAM) fMode, 0); }


RichEdit自己的函数都是通过SendMessage实现的,这些函数是线程安全的。
GetWindowText是CWnd的函数,这个不能跨线程操作。

int CWnd::GetWindowText(_Out_z_cap_post_count_(nMaxCount, return + 1) LPTSTR lpszString, _In_ int nMaxCount) const
{
ASSERT(::IsWindow(m_hWnd) || (m_pCtrlSite != NULL));

if (m_pCtrlSite == NULL)
return ::GetWindowText(m_hWnd, lpszString, nMaxCount);
else
{
CString str;

m_pCtrlSite->GetWindowText(str);
Checked::tcsncpy_s(lpszString, nMaxCount, str, _TRUNCATE);
return lstrlen(lpszString);
}
}


所以你应该用CRichEditCtrl自己的成员函数就没问题了,就算debug定位到错误行,也是莫名其妙的位置,没什么意义。
我字多,给分吧。
那为啥程序在有些电脑上能正常运行有些不行
回复
mmcanyu 01-09
那还发消息的方式吧

线程中
CString *pstr = new CString("......");
COLORREF color = RGB(255,0,0);
::PostMessage(hMsgWnd, (WPARAM)pstr, (LPARAM)color);

窗口收到消息之后
CString *pstr = (CString *)WParam;
.......
delete pstr;

这里用PostMessage是怕滥用SendMessage发生锁死的现象,比如窗口在等线程结束,同时线程又在等窗口返回。
回复
mmcanyu 01-08
应该还是线程操作的问题。
打开RichEdit的源文件afxcmn.inl看一下,我贴一部分上来。

_AFXCMN_INLINE BOOL CRichEditCtrl::CanUndo() const
{ ASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_CANUNDO, 0, 0); }
_AFXCMN_INLINE BOOL CRichEditCtrl::CanRedo() const
{ ASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_CANREDO, 0, 0); }
_AFXCMN_INLINE UNDONAMEID CRichEditCtrl::GetUndoName() const
{ ASSERT(::IsWindow(m_hWnd)); return (UNDONAMEID) ::SendMessage(m_hWnd, EM_GETUNDONAME, 0, 0); }
_AFXCMN_INLINE UNDONAMEID CRichEditCtrl::GetRedoName() const
{ ASSERT(::IsWindow(m_hWnd)); return (UNDONAMEID) ::SendMessage(m_hWnd, EM_GETREDONAME, 0, 0); }
_AFXCMN_INLINE int CRichEditCtrl::GetLineCount() const
{ ASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_GETLINECOUNT, 0, 0); }
_AFXCMN_INLINE BOOL CRichEditCtrl::GetModify() const
{ ASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_GETMODIFY, 0, 0); }
_AFXCMN_INLINE void CRichEditCtrl::SetModify(BOOL bModified /* = TRUE */)
{ ASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETMODIFY, bModified, 0);}
_AFXCMN_INLINE BOOL CRichEditCtrl::SetTextMode(UINT fMode)
{ ASSERT(::IsWindow(m_hWnd)); return (BOOL) ::SendMessage(m_hWnd, EM_SETTEXTMODE, (WPARAM) fMode, 0); }


RichEdit自己的函数都是通过SendMessage实现的,这些函数是线程安全的。
GetWindowText是CWnd的函数,这个不能跨线程操作。

int CWnd::GetWindowText(_Out_z_cap_post_count_(nMaxCount, return + 1) LPTSTR lpszString, _In_ int nMaxCount) const
{
ASSERT(::IsWindow(m_hWnd) || (m_pCtrlSite != NULL));

if (m_pCtrlSite == NULL)
return ::GetWindowText(m_hWnd, lpszString, nMaxCount);
else
{
CString str;

m_pCtrlSite->GetWindowText(str);
Checked::tcsncpy_s(lpszString, nMaxCount, str, _TRUNCATE);
return lstrlen(lpszString);
}
}


所以你应该用CRichEditCtrl自己的成员函数就没问题了,就算debug定位到错误行,也是莫名其妙的位置,没什么意义。
我字多,给分吧。
回复
引用 3 楼 schlafenhamster 的回复:
检查 strLog
strLog是为了换行用的
回复
远程调试工具安装哪个??
回复
zgl7903 01-08
试试修改下参数类型 void AddLogString(CString &strLog,COLORREF color) 调试版本, 在奔溃的机器上 安装远程调试器,远程调试下
回复
检查 strLog
回复
对,是在子线程中有操作,调用函数如下,不过软件在有的电脑上能正常运行,在有的电脑上不能,不知道咋回事 void IMEICHECKDlg::AddLogString(CString strLog,COLORREF color) { CString log = CurrentTime(); strLog += _T("\r\n"); int nOldLines = 0; int nNewLines = 0; int nScroll = 0; long nInsertionPoint = 0; CHARFORMAT cf; nOldLines = m_richEdit.GetLineCount(); cf.cbSize = sizeof(CHARFORMAT); cf.dwMask = CFM_COLOR; cf.dwEffects = 0; cf.crTextColor = RGB(0,0,0); nInsertionPoint = m_richEdit.GetWindowTextLengthW(); nInsertionPoint = -1; m_richEdit.SetSel(nInsertionPoint, -1); m_richEdit.SetSelectionCharFormat(cf); m_richEdit.ReplaceSel(log); cf.crTextColor = color; nInsertionPoint = m_richEdit.GetWindowTextLengthW(); m_richEdit.SetSel(nInsertionPoint, -1); m_richEdit.SetSelectionCharFormat(cf); m_richEdit.ReplaceSel(strLog); nNewLines = m_richEdit.GetLineCount(); nScroll = nNewLines - nOldLines; m_richEdit.LineScroll(nScroll); return; }
回复
mmcanyu 01-08
线程中直接操作的吗?
回复
相关推荐
发帖
界面
创建于2007-09-28

1.5w+

社区成员

VC/MFC 界面
申请成为版主
帖子事件
创建了帖子
2021-01-08 03:09
社区公告
暂无公告