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

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;
}

还请各位朋友看看,我的思路对吗?
为什么依然有那样的缺陷?
...全文
564 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)

15,471

社区成员

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

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