*** 一个MDIGetActive()调用失败的问题……关键字:FromHandle,IsKindOf,GetActiveView

silver 2001-03-28 11:42:00
加精

在一个MDI程序的视图类中启动一个工作者线程,代码如下:

CMainFrame* pFrame=(CMainFrame*)(AfxGetApp()->m_pMainWnd);
if(pFrame==NULL)
MessageBox("无法得到主窗口句柄");
else
AfxBeginThread(COMPILE,pFrame);


该线程过程开始部分代码如下:

UINT COMPILE(LPVOID pParam)
{
CMainFrame* pFrame=(CMainFrame*)pParam;
// >>>>> 问题就出在下面的MDIGetActive上
CChildFrame *pCFrame=(CChildFrame*)(pFrame->MDIGetActive());
if(pCFrame==NULL) return 1;
CMyAppView *pView=(CMyAppView *)pCFrame->GetActiveView();
if(pView==NULL) return 1;

…………
}


MDIGetActive()调用失败,跟进的结果发现问题出在MDIGetActive里面调用的FromHandle上,FromHandle返回了一个临时的窗口句柄,在紧接着的ASSERT IsKindOf 的时候就出错了,这两句代码如下:

CMDIChildWnd* CMDIFrameWnd::MDIGetActive(BOOL* pbMaximized) const
{
…………

CMDIChildWnd* pWnd = (CMDIChildWnd*)CWnd::FromHandle(hWnd);
ASSERT(pWnd == NULL || pWnd->IsKindOf(RUNTIME_CLASS(CMDIChildWnd)));

…………
return pWnd;
}

有人遇到过这样的问题吗?300分?求解决方案!


...全文
347 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
silver 2001-03-29
  • 打赏
  • 举报
回复
谢谢joke100,虽然我用了其他方法,但是joke100提供的方法显然是最标准的

先放一天,大家看看,明天我来加分
xyzboat 2001-03-29
  • 打赏
  • 举报
回复
我靠,上了一天我都上不来,终于上来了.
问题Joke100已经说明了,主要是因为错用了Worker Thread.
窗口,像Socket一样,作为一种资源,是线程独享的,直接把指针传过去虽然得到了正确的指针(没有与窗口相关的操作还是可以完成,比如提供一个方法),但却出现了越界访问.
解决方法:
1.确保你的线程是从CWinThread继承而来的,而不是一个简单UINT 函数.
2.资源都要传递句柄.

这个教训是深刻的,因为我已经在此犯了一次错了(上次是Socket)
关于那些是资源,在MSDN上有说明,建议大家都看看.
共同进步!!!
joke100 2001-03-28
  • 打赏
  • 举报
回复
CChildFrame是CMDIChildWnd的派生类吗?
在每个函数内部的使用好象没有什么不妥的地方呃....


joke100 2001-03-28
  • 打赏
  • 举报
回复
SORRY!!!!!!!!!!!!!!!!
UINT COMPILE(LPVOID pParam)传进的参数没有具体的类型,没办法使用
CMainFrame* pFrame = dynamic_cast<CMainFrame*>(pParam);
还是用强制转换吧....

一般来讲问题应该是传进的参数不合法...



xyzboat 2001-03-28
  • 打赏
  • 举报
回复
CChildFrame *pCFrame=(CChildFrame*)(pFrame->MDIGetActive());
改成CMDIChildWnd *pCFrame= (CMDIChildWnd*)(pFrame->MDIGetActive());

也不行吗.
看你的程序好象没什么错误呀.
joke100 2001-03-28
  • 打赏
  • 举报
回复
是的,
CWnd::FromHandle返回的指针只是临时使用,不要保存它...
The pointer may be temporary and should not be stored for later use.

取框架可以使用全局函数
CWnd* AfxGetMainWnd();

确定你在UINT COMPILE(LPVOID pParam)传进的参数是正确的吗???
CMainFrame* pFrame=(CMainFrame*)pParam;???
使用强制转换其实并不安全...
CMainFrame* pFrame = dynamic_cast<CMainFrame*>(pParam);
if (NULL == pFrame)
...



joke100 2001-03-28
  • 打赏
  • 举报
回复
确实是多线程的问题。
为达到保护的目的,从句柄到实体的映射不同的线程是不同的。

As a general rule, a thread can access only MFC objects that it created. This is because temporary and permanent Windows handle maps are kept in thread local storage to ensure protection from simultaneous access from multiple threads. For example, a worker thread cannot perform a calculation and then call a document’s UpdateAllViews member function to have the windows that contain views on the new data modified. This will have no effect at all, because the map from CWnd objects to HWNDs is local to the primary thread. This means that one thread may have a mapping from a Windows handle to a C++ object, but another thread may map that same handle to a different C++ object. Changes made in one thread would not be reflected in the other.


解决:
To pass these objects from one thread to another, always send them as their native HANDLE type.
to pass individual handles (such as an HWND) rather than C++ objects to the worker thread. The worker thread then adds these objects to its temporary map by calling the appropriate FromHandle member function. You could also add the object to the thread’s permanent map by calling Attach, but this should be done only if you are guaranteed that the object will exist longer than the thread.

试一试这样如何???
******************************
...
AfxBeginThread(COMPILE,pFrame->m_hWnd);

// 参数是CMainFrame对象的HANDLE,传入HANDLE,进来后再构造线程本地对象
UINT COMPILE(LPVOID pParam)
{
CMainFrame* pFrame = (CMainFrame*)CWnd::FromHandle(hWnd);
CChildFrame *pCFrame=(CChildFrame*)(pFrame->MDIGetActive());
if(pCFrame==NULL || pCFrame->IsKindOf(RUNTIME_CLASS(CMyObject)))
return 1;
CMyAppView *pView=(CMyAppView *)pCFrame->GetActiveView();
if(pView==NULL) return 1;

…………
}
silver 2001-03-28
  • 打赏
  • 举报
回复
出错的这段代码
CMainFrame* pFrame=(CMainFrame*)pParam;
CChildFrame *pCFrame=(CChildFrame*)(pFrame->MDIGetActive());
if(pCFrame==NULL) return 1;
CMyAppView *pView=(CMyAppView *)pCFrame->GetActiveView();
if(pView==NULL) return 1;

如果不放在线程过程里面,放在其他地方是没有什么问题的
silver 2001-03-28
  • 打赏
  • 举报
回复
joke100(joke100): 我检查了一下pFrame,这个指针应该没有什么问题,CChildFrame是CMDIChildWnd的派生类,会不会和线程有关系?

16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

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