关于同一进程内不同对话框分线程显示的疑问

Fireway2008 2009-06-18 10:55:38
加精
各位朋友,本人曾发表过一篇帖子:
关于自封装的消息循环函数的改进问题


虽然承蒙众多高手的指导,但是依旧存在一些小bug。
尤其对于 从主对话框用新线程打开新的对话框S后,在这个对话框S关闭并销毁后,主对话框无法响应鼠标点击的问题,至今依然未找到原因……

对于新的一个基于对话框的MFC工程Test,本人有了新的设想:
1. 这个工程内有3个对话框,主对话框0,启动画面对话框A,初始界面对话框B

2. 本人打算实现这样的效果,
把此工程的主对话框0创建成一个非模式对话框,以便让其创建完成后,不会立即显示;
在CTestApp内,开辟新的线程让启动画面对话框A出现并短暂地停留2s,然后销毁,并打开初始化界面对话框B
因为主对话框0内部处理过程比较繁琐而复杂,所以需要让对话框A和B显示的时候拖延时间给主对话框完成创建,然后点击初始化对话框B上的某个按钮,利用ShowWindow(SW_SHOW)把主对话框0给显示出来,这样看起来显得界面的呈现比较平滑有序。

3. 目前,为了这个目的,本人按如下代码做了如下尝试:

BOOL CTestApp::InitInstance()
{
AfxEnableControlContainer();
//必须新开一线程打开启动页面
::AfxBeginThread(OpenBgPageThread, this, THREAD_PRIORITY_HIGHEST);


// 创建主对话框
m_pMainDlg = new CAdoRWAccessDlg(IDD_ADORWACCESS_DIALOG);
m_pMainWnd = m_pMainDlg;


m_pMainDlg->Create(IDD_ADORWACCESS_DIALOG, CWnd::GetDesktopWindow());

return TRUE;// 声明对话框创建成功
}

UINT CTestApp::OpenBgPageThread(LPVOID lpv)
{
CBeginDlg dlg;
dlg.DoModal();
::AfxEndThread(0);
return 0;
}

在启动画面对话框A内,设 定时器
SetTimer(ID_CLOSE_BEGINDLG, 2000, NULL);

void CBeginDlg::OnTimer(UINT nIDEvent)
{
this->KillTimer(ID_CLOSE_BEGINDLG);
this->OnOK();//销毁此对话框并开启新的对话框B

OpenStPage();

}
void CBeginDlg::OpenStPage()
{
//CFriendBookADODlg* m_pStPageDlg;
m_pStPageDlg = new CFriendBookADODlg; //我是用new创建的


CWinThread* pThread=::AfxBeginThread(OpenStPageThread, (LPVOID)m_pStPageDlg, THREAD_PRIORITY_HIGHEST);
if(pThread)
{
((CFriendBookADOApp*)AfxGetApp())->m_pThreadList.push_back(pThread);
}
}

UINT CBeginDlg::OpenStPageThread(LPVOID lpv)
{
if(lpv == NULL)
{
AfxMessageBox(_T("ERROR"), MB_OK| MB_ICONERROR);
::AfxEndThread(2);
}

CFriendBookADODlg* dlg = (CFriendBookADODlg*)lpv;
// 初始化对话框B为无模式对话框
dlg->Create(IDD_FRIENDBOOKADO_DIALOG, CWnd::GetDesktopWindow());
dlg->ShowWindow(SW_SHOW);

dlg->DoLoop();
// 这里为了测试消息循环是否正常,特地加入了如下语句
AfxMessageBox(_T("quit while!"));

delete dlg;
((CFriendBookADOApp*)AfxGetApp())->m_pThreadList.remove(AfxGetThread());
return 0;
}

4. 然而,程序运行的结果出乎我的意料!
在初始化对话框B出来后,按理应该进入了消息循环,可是,
我并没有给其投入PostQuitMessage(0);


可是它却自动跳出了消息循环,运行到了AfxMessageBox(_T("quit while!"));
接下来 则执行delete dlg;

而我并没有打算让初始化对话框B 这个时候销毁,它还有用的上的地方呢 !


5. 按上一个帖子,我更正了DoLoop为如下代码:
BOOL CFriendBookADODlg::DoLoop()
{
MSG msg;
while(1)
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if(msg.message==WM_QUIT) //只有当收到这个消息才退出
break;

TranslateMessage(&msg);
DispatchMessage(&msg);
}

else
{
//在这里添加空闲任条处理
//之前的代码是
AfxGetApp()->OnIdle(0);
AfxGetApp()->OnIdle(1);
//目前换成如下,但还是有问题……

Sleep(100);
}
}
return TRUE;
}

还请各位朋友看看,我的思路对吗?
为什么依然有那样的缺陷?
...全文
561 59 打赏 收藏 转发到动态 举报
写回复
用AI写文章
59 条回复
切换为时间正序
请发表友善的回复…
发表回复
Fireway2008 2009-07-12
  • 打赏
  • 举报
回复
[Quote=引用 51 楼 cnzdgs 的回复:]
对比一下两者消息循环有何差异。
[/Quote]
找到问题所在了!!
以下把代码贴出来与大伙分享!
BOOL CBDlg::DoLoop()
{
MSG msg;
while(1)
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if(msg.message==WM_QUIT)
{
MessageBox(_T("收到WM_QUIT消息,即将quit while"),_T("注意了"),MB_OK);
break;
}
// 处理Tip消息,m_bt为我的一个按钮类对象
m_bt.m_toolTip.Activate (TRUE);
m_bt.m_toolTip.RelayEvent (&msg);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//在这里添加空闲任条处理
Sleep(100);
}
}
return TRUE;
}
Fireway2008 2009-07-11
  • 打赏
  • 举报
回复
最终决定,在特殊的对话框内,还是用系统提供的DoModal之类的函数来创建,
自写的消息循环要处理很多信息,没必要也没价值。


感谢各位的参与帮助!!
rularys 2009-07-10
  • 打赏
  • 举报
回复
MFC
Fireway2008 2009-07-08
  • 打赏
  • 举报
回复
最近研究了一下,
在线程内创建对话框B的时候,不用自己写的DoLoop,而用VC内的DoModal。
发现49楼的问题没有再出现。可是,关闭了这个对话框后,发现主对话框又无法相应鼠标点击的消息了,和上一个贴子70楼的问题一模一样!

!(关于自封装的消息循环函数的改进问题

仔细查找了一下,发现自己为了顺利销毁 初始化对话框B 并正常退出其DoLoop消息循环,
重载了WindowProc这个函数。如下:

LRESULT CBDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
/*请大家帮忙注意看我写的代码,是否存在问题??? */
switch(message)
{
case WM_CLOSE:
MessageBox(_T("收到WM_CLOSE消息,即将调用 DestroyWindow(hwnd);"),_T("注意了"),MB_OK);
DestroyWindow();
break;
case WM_DESTROY:
MessageBox(_T("已调用过DestroyWindow(hwnd),看不到窗口了吧;"),_T("注意了"),MB_OK);
MessageBox(_T("以下处理WM_DESTROY"),_T("注意了"),MB_OK);
PostQuitMessage(0);
break;
case WM_SYSCOMMAND:
if((LOWORD(wParam)&0xFFF0) == SC_CLOSE)
{
MessageBox(_T("你关闭了窗口,即将发送WM_CLOSE消息!"),_T("注意了"),MB_OK);
SendMessage(WM_CLOSE,0,0);
}
//不要添加 break,否则不能响应其它WM_SYSCOMMAND消息
}

return CDialog::WindowProc(message, wParam, lParam);
}


后来,索性把上边的代码注释掉,仅仅靠DoModal自带的消息循环进行处理,关闭界面B后,主对话框再次得到了鼠标消息的点击响应。



我认为关键在于深入理解DoModal的实现,请问哪问高手可以给个专门解说DoModal机制的网址,供本人学习参考?
谢谢!
daiafei 2009-07-07
  • 打赏
  • 举报
回复
值得学习~~~~
tempname1008 2009-07-06
  • 打赏
  • 举报
回复
关注。。。
Fireway2008 2009-07-03
  • 打赏
  • 举报
回复
[Quote=引用 50 楼 WooSir 的回复:]
你用的CTipToolCtrl和MFC的CToolTipCtrl是一回事吗?还是第三方按钮带的第三方类?
反正没见过你所述的奇怪问题。
[/Quote]

不好意思,打错了控件名称,应该是CToolTipCtrl。
Fireway2008 2009-07-02
  • 打赏
  • 举报
回复
最后,本人还打算提个问题:

初始界面对话框B,我在CTestApp::InitInstance()通过2种不同的方式创建,可是,他们的效果截然不同!

	
BOOL CTestApp::InitInstance()
{
…………
…………
// 前略,和40楼的代码一致
m_bCreateSuc = TRUE;

//-----------------------------------------------------------------------------------//
// 等待主对话框创建成功后,初始界面对话框B
// 方法1. 通过UI线程创建
//OpenStPage();
// 方法2. 通过模式对话框DoModal创建
m_pStPageDlg = new CFriendBookADODlg;
m_pStPageDlg->DoModal();
return TRUE;

现象如下:
1.
用方法1创建的时候,发现B界面上的按钮(带CTipToolCtrl)
把鼠标指向按钮的时候,无法弹出提示;
2.
而用方法2创建的界面,把鼠标指向按钮的时候,却可以弹出提示;


3.最值得一提的是:
初始界面对话框B上有个按钮,用于弹出软件信息的。有个About按钮,点击后响应消息,按如下代码生成一个对话框,

void CFriendBookADODlg::OnButtonAbout()
{
CAboutDlg dlg;
dlg.DoModal();
}

当用方法1创建界面B的时候,点击About按钮
软件信息的对话框弹出来后,便处于界面B之上,
若这个时候把鼠标指向
界面B其他按钮,却发现其上每个按钮的Tip都可以顺利弹出来。


而当把About按钮创建的软件信息对话框关闭后,把鼠标移动到界面B任意一个按钮上的时候,发现按钮的Tip又无法出来了……

我想一定有人也遇到过类似的问题吧,请问如何解决呢??
Fireway2008 2009-07-02
  • 打赏
  • 举报
回复
[Quote=引用 42 楼 WooSir 的回复:]
void CAdoRWAccessDlg::OnCancel()
{
CDialog::OnCancel();//没必要
DestroyWindow();
}
退出主程序的时候,用PostThreadMessage发WM_QUIT给CCOverThread。

建议:线程和线程之间,窗口与窗口之间通过互发消息和全局变量通讯(可定义全局的或类成员的窗口句柄HWND,线程ID),不要直接用指针指来指去,容易出现同步问题,且不小心会泄露内存。
[/Quote]
这位朋友说的是阿,
在主对话框内,我通过如下语句,顺利地把这个线程给结束掉了,而且不再出现内存泄露现象。

void CTestDlg::OnDestroy()
{
m_pCovThread->PostThreadMessage(WM_QUIT, 0, 0);
CDialog::OnDestroy();
}
huohuo1120 2009-07-02
  • 打赏
  • 举报
回复
关注,学习。
cnzdgs 2009-07-02
  • 打赏
  • 举报
回复
对比一下两者消息循环有何差异。
WooSir 2009-07-02
  • 打赏
  • 举报
回复
你用的CTipToolCtrl和MFC的CToolTipCtrl是一回事吗?还是第三方按钮带的第三方类?
反正没见过你所述的奇怪问题。
Fireway2008 2009-07-01
  • 打赏
  • 举报
回复
[Quote=引用 46 楼 Tomzzu 的回复:] 在线程中, 每个线程函数的while循环中, 都应加上如sleep(300), 把300毫秒时间交给操作系统, 如果不交可能线程一直占着CPU 100%
[/Quote]

这位朋友,你认为Sleep(300); 追加在哪个位置为好?目前我的消息循环按如下书写。
//////////////////////////////////////////////////////////////////////////////
//名称:DoLoop
//功能:实现窗体消息循环
/////////////////////////////////////////////////////////////////////////////
BOOL CMyDlg::DoLoop()
{
MSG msg;
while(1)
{
if(PeekMessage(&msg, NULL, 0,0, PM_REMOVE))
{
if(msg.message == WM_QUIT)
{
MessageBox(_T("收到WM_QUIT消息,即将quit while"),_T("注意了"),MB_OK);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//在这里添加空闲任条处理
Sleep(100);
}
}
return TRUE;
}
Tomzzu 2009-07-01
  • 打赏
  • 举报
回复
1. 在多线程中如果调用的窗体 如果是非模态的, 还没等窗体显示出来线程可能就结束了
所以线程函数的while循环中要不停的循环判断, 直到窗体该结束时再退出线程

2. 在线程中, 每个线程函数的while循环中, 都应加上如sleep(300), 把300毫秒时间交给操作系统, 如果不交可能线程一直占着CPU 100%
  • 打赏
  • 举报
回复
看来39楼分析的对.高手
Fireway2008 2009-06-30
  • 打赏
  • 举报
回复
[Quote=引用 43 楼 qingfeng5211 的回复:]
不知道CFriendBookADOApp这个类是哪里来的,我按照你的代码见了工程,结果是在DoLoop里循环啊,没有quit while。你要的话把代码发给你。jhf_uptech@126.com
[/Quote]
哦, 这个类是多余的,没有这个类,目前测试工程只有 CTestApp.

而且,按照40楼的代码,消息循环没问题了。
qingfeng5211 2009-06-30
  • 打赏
  • 举报
回复
不知道CFriendBookADOApp这个类是哪里来的,我按照你的代码见了工程,结果是在DoLoop里循环啊,没有quit while。你要的话把代码发给你。jhf_uptech@126.com
WooSir 2009-06-28
  • 打赏
  • 举报
回复
void CAdoRWAccessDlg::OnCancel()
{
CDialog::OnCancel();//没必要
DestroyWindow();
}
退出主程序的时候,用PostThreadMessage发WM_QUIT给CCOverThread。

建议:线程和线程之间,窗口与窗口之间通过互发消息和全局变量通讯(可定义全局的或类成员的窗口句柄HWND,线程ID),不要直接用指针指来指去,容易出现同步问题,且不小心会泄露内存。
Fireway2008 2009-06-28
  • 打赏
  • 举报
回复
[Quote=引用 38 楼 cnzdgs 的回复:]
IMPLEMENT_DYNCREATE(CCOverThread, CWinThread)要看线程是否正常退出,CCOverThread::ExitInstance是否执行,与CFriendBookADOApp::ExitInstance无关。
m_pOwner->m_pCoverDlg = new CCOverDlg;对应的是delete m_pOwner->m_pCoverDlg,在CTestDlg中delete this与此无关。
[/Quote]

恩,目前,我的确是做了这样的处理,可是问题依旧……


BOOL CCOverThread::InitInstance()
{
m_pOwner->m_pCoverDlg = new CCOverDlg;
m_pOwner->m_pCoverDlg->m_pMainPage = m_pOwner;
m_pOwner->m_pCoverDlg->Create(IDD_COVER_DIALOG, NULL);
m_pOwner->m_pCoverDlg->PaintWindow();
m_pMainWnd = m_pOwner->m_pCoverDlg;
return TRUE;
}

int CCOverThread::ExitInstance()
{
if(m_pOwner->m_pCoverDlg != NULL)
{
m_pOwner->m_pCoverDlg->DestroyWindow();

//按此方式删除,可是在利用如下代码
///////////////////////////////////////
void CAdoRWAccessDlg::OnCancel()
{
CDialog::OnCancel();
DestroyWindow();
}
///////////////////////////////////////
//退出主程序的时候,一直不能进入
int CCOverThread::ExitInstance()这个函数内。想不明白是为什么,感觉很茫然。
delete m_pOwner->m_pCoverDlg;
m_pMainWnd = NULL;
}

return CWinThread::ExitInstance();
}
Fireway2008 2009-06-28
  • 打赏
  • 举报
回复
[Quote=引用 39 楼 WooSir 的回复:]
看你最初的代码
第一个线程 OpenBgPageThread 开启的对话框A内
void CBeginDlg::OnTimer(UINT nIDEvent)
{
this->KillTimer(ID_CLOSE_BEGINDLG);
this->OnOK();//销毁此对话框并开启新的对话框B
OpenStPage();
}
第二个线程OpenStPage开启新的对话框B

显然第二个线程由第一个线程创建,为子线程,可是“this->OnOK();”(实际调用EndDialog(IDOK),导致DoModal退出)之后,实际dlg将销毁,第一个线程 继续以下…
[/Quote]

这位朋友分析得很对阿!
目前,我按如下处理了,在主程序的App内:




CFriendBookADOApp theApp;

/////////////////////////////////////////////////////////////////////////////
// CFriendBookADOApp initialization

BOOL CTestApp::InitInstance()
{
AfxEnableControlContainer();

// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.

#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif

//-----------------------------------------------------------------------------------//
//1.必须新开一线程打开启动页面
::AfxBeginThread(OpenBgPageThread, this, THREAD_PRIORITY_HIGHEST);
//2.创建主对话框
m_pMainDlg = new CAdoRWAccessDlg(IDD_ADORWACCESS_DIALOG);
m_pMainWnd = m_pMainDlg;
srand( (unsigned)time(NULL));
int i = rand()% 2;
i=1;
//-----------------------------------------------------------------------------------//
// 随机打开不同风格内页面
if (i == 0)
{
m_pMainDlg->m_nBkgndMode = 1;
m_pMainDlg->Create(IDD_ADORWACCESS_DIALOG, NULL);

}else{
m_pMainDlg->m_nBkgndMode = 2;
m_pMainDlg->Create(IDD_ADORWACCESS_DIALOG1,NULL);
}
//标示主对话框创建完成
m_bCreateSuc = TRUE;

//-----------------------------------------------------------------------------------//
//3.等待主对话框创建成功后,打开页面B
OpenStPage();

return TRUE;
}
// 目前做了如下改动,这个时候,发现一切正常了!
void CTestApp::OpenStPage()
{
m_pStPageDlg = new CFriendBookADODlg;

CWinThread* pThread=::AfxBeginThread(OpenStPageThread, (LPVOID)m_pStPageDlg, THREAD_PRIORITY_HIGHEST);

}

UINT CTestApp::OpenStPageThread(LPVOID lpv)
{
if(lpv == NULL)
{
AfxMessageBox(_T("ERROR"), MB_OK| MB_ICONERROR);
::AfxEndThread(2);
return -1;
}

CFriendBookADODlg* dlg = (CFriendBookADODlg*)lpv;
dlg->Create(IDD_FRIENDBOOKADO_DIALOG, CWnd::GetDesktopWindow());
dlg->ShowWindow(SW_SHOW);
dlg->DoLoop();
AfxMessageBox("quit loop!");

delete dlg;
::AfxEndThread(0);

return 0;
}

UINT CTestApp::OpenBgPageThread(LPVOID lpv)
{
CBeginDlg dlg;
dlg.DoModal();
::AfxEndThread(0);
return 0;
}
加载更多回复(39)
依托Android最新的主流平台为Android4.0,面向最新的SDK和相关工具,同时还涵盖了老版平台中的一些常用功能,以帮助开发人员适应目前市面上所有的主流设备。《Android移动应用开发(第3版)卷Ⅰ:基础篇》涵盖了作者数年来在移动开发领域所积累的经验,以及成功进行移动项目开发所需的全部概念和实用技术,包括移动开发过程与传统软件开发的区别,还包括帮助你节约宝贵时间和规避隐患的技巧。不管你的项目有多大规模,《Android移动应用开发(第3版)卷Ⅰ:基础篇》都能为你提供帮助。 内容推荐   《Android移动应用开发(第3版)卷Ⅰ:基础篇》涵盖了Android开发从概念、技术到市场推广应用的全部主题,内容包括Android平台概览、Android应用程序设计精髓、Android用户界面设计精髓、Android应用程序设计精髓、对外发布你的Android应用程序和附录6个部。附录还包含了Android开发常用工具(模拟器、DDMS和Eclipse)的使用指南。   《Android移动应用开发(第3版)卷Ⅰ:基础篇》卷Ⅰ以及本书的卷Ⅱ,不仅适合Android应用程序开发人员阅读,还能为QA测试人员提供指导。另外,本书还可以帮助项目经理更好地管理项目进程和项目团队,帮助市场推广人员更好地把握市场,创造令人瞩目的销售业绩。 作者简介 Lauren Darcey LaurenDarcey是一家专注于移动开发的软件公司的技术主管,所涉及的技术包括Android、iPhone、Blackberry、PalmPre、BREW和J2ME,还包括咨询服务。拥有20年专业软件产品研发经验的Lauren被公认为企业架构和商业级移动应用开发的权威。Lauren获得了加州大学圣克鲁兹校授予的计算机科学专业学士学位。Shane Conder ShaneConder拥有丰富的开发经验,在过去的10年中,他把主要精力集中在移动和嵌入式开发领域,迄今已设计并开发了基于Android、iPhone、BREW、Blackberry、J2ME、Palm和WindowsMobile的诸多商业软件。Shane撰写了大量关于移动通信行业和评估移动开发平台的文章,这些文章发表在他的个人技术博客上,在业界受到了广泛的关注。Shane获得了加州大学授予的计算机科学专业学士学位。 目录 第一部 Android平台概览 第1章 Android简介  第一部 Android平台概览 第1章 Android简介  第2章 搭建你的Android开发环境  第3章 编写第一个Android应用程序  第4章 掌握Android开发工具  第二部 Android应用程序设计基础 第5章 剖析Android应用程序  第6章 使用Android Manifest文件定义应用程序  第7章 管理应用程序资源  第三部 Android用户界面设计精髓 第8章 探索用户界面屏幕元素  第9章 使用布局设计用户界面  第10章 使用Fragment  第11章 使用对话框  第四部 Android应用程序设计精髓 第12章 使用Android首选项  第13章 使用文件和目录  第14章 使用内容提供器  第15章 设计高兼容性应用程序  第五部 对外发布你的Android应用程序 第16章 移动软件开发过程  第17章 设计和开发"防弹"Android应用程序  第18章 测试Android应用程序  第19章 发布你的Android应用程序  第六部 附录 附录A Android模拟器快速入门  A.1 模拟器的目的:虚拟现实  A.2 使用Android虚拟设备(AVD)  A.2.1 使用Android SDK和AVD管理器  A.2.2 创建AVD  A.3 使用指定AVD启动模拟器  A.3.1 维持模拟器高效运行  A.3.2 配置模拟器启动项  A.3.3 启动模拟器运行应用程序  A.3.4 从Android SDK和AVD管理器中启动模拟器  A.4 配置模拟器的GPS位置信息  A.5 在两个模拟器实例之间拨打电话  A.6 在两个模拟器实例之间发送消息  A.7 通过控制台与模拟器交互  A.7.1 使用控制台模拟来电  A.7.2 使用控制台模拟短消息  A.7.3 使用控制台发送GPS坐标  A.7.4 使用控制台监视网络状态  A.7.5 使用控制台操作电源设置  A.7.6 使用其他控制台命令  A.8 享受模拟器  A.9 认识模拟器的局限  参考资料和更多信息  附录B Android DDMS快速入门  B

15,472

社区成员

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

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