爱收集资源网

点击确定,即刻退出

网络整理 2023-09-25 22:05

因为Windows上的模态对话框为众人所知,因此本文的事例都是指Windows上的,并且有时候会特指是MFC的。

众所周知,当模态窗口被打开以后,正常的流程会暂时挂起,或者浅显一点说,程序挪开了,直到模态窗口关掉才能继续执行。例如下边这段代码:

CInputDialogdlg;

if(dlg.DoModal() == IDOK)

//执行按了确定按键退出的流程

else

//执行通过别的方法退出的流程,例如按了取消按键

//继续执行

在这段代码里,在CInputDialog窗口关掉之前,注释部份的代码是不会得到执行的。

接下来请先思索一个问题,为什么调用了dlg.DoModal()之后,程序会挪开呢?

首先,不可能是线程被挂起,因为通常情况,只有一个主线程,如果线程挂起,那就哪些也做不了了,但其实模态窗口弹下来以后还是可以做好多事情的。

其次,也不可能是用类似于Sleep之类的函数,让程序等待,和线程挂起一样。

如 果我们了解Windows应用程序的运行的原理,了解消息分发的机制,就可以晓得,UI线程有一个消息循环,通过GetMessage之类的函数获取消 息,并且分发。如果没有这个消息循环,整个窗口系统就难以正常工作。很显然,当有模态窗口打开的时侯,整个窗口系统还是正常工作的,因此可以确定,此时消 息循环一定还在正常运行着。这个消息循环在那里呢?因为当模态对窗口弹下来以后,程序就暂停了,相当调用模态窗口的函数仍然没有返回,那么也就没有机会再 进入缺省消息循环了,这究竟是如何回事呢?福尔摩斯常常说:“除去不可能的剩下的虽然再不可能,那也是真相。”基于这个道理,真像只有一个,就是模态窗口 内部有一个消息循环,负责消息的接收和转发。

为了证明这个说法,可以做个试验,弹出一个模态对话框,并设置合适的断点,查看堆栈。

使用DialogBox(NULL, MAKEINTRESOURCE(IDD_MAINDLG), m_hWnd, DialogProc);语句弹出对话框,并且在DialogProc里设置一个合适的断点,我们可以在堆栈中听到这样的信息:

ZK.exe!CMainDlg::DialogProc(HWND__ * hwndDlg=0×000411e0, unsigned int uMsg=0×00000201, unsigned int wParam=0×00000001, long lParam=0×003a009c)行90 C++

user32.dll!_InternalCallWinProc@20()+ 0×23字节

user32.dll!_UserCallDlgProcCheckWow@32()+ 0xa9字节

user32.dll!_DefDlgProcWorker@20()+ 0×7f字节

user32.dll!_DefDlgProcW@16()+ 0×22字节

user32.dll!_InternalCallWinProc@20()+ 0×23字节

user32.dll!_UserCallWinProcCheckWow@32()+ 0xb3字节

user32.dll!_DispatchMessageWorker@8()+ 0xe6字节

user32.dll!_DispatchMessageW@4()+ 0xf字节

user32.dll!_IsDialogMessageW@8()- 0xeaa7字节

user32.dll!_DialogBox2@16()+ 0xc0字节

user32.dll!_InternalDialogBox@24()+ 0xb6字节

user32.dll!_DialogBoxIndirectParamAorW@24()+ 0×36字节

user32.dll!_DialogBoxParamW@20()+ 0×3f字节

ZK.exe!CMainDlg::OnOK(unsigned short __formal=0×0000, unsigned short wID=0×0001, unsigned short __formal=0×0000, unsigned short __formal=0×0000)行98 + 0×1d字节C++

上面的堆栈信息中,红色加粗的函数是API函数IsDialogMessage,这个函数的第二个参数是LPMSGlpMsg,这个正是从GetMessage返回的当前消息的结构体。可以想象,在DialogBox函数内部的实现里,在调用IsDialogMessage之前,必定先通过GetMessage之类的函数,从消息队里返回了当前的消息了。

到 了这儿,我们基本可以确定,在模态窗口内部,也实现了一个消息循环,真是这个消息循环接管了线程中缺省的消息循环,使整个窗口系统能继续正常的工作。同时 由于消息循环虽然也是一个有退出条件的死循环,因此到这个循环结束之前(一般是关掉了模态窗口),模态窗口旁边的代码是不会继续执行的。

理解了模态窗口的原理,就可以在任何支持消息队列的GUI系统中,加入模态窗口的机制,这会降低好多开发工作。例如好多手机平台不支持模态窗口模态对话框是什么时候实例的对话框模态对话框是什么时候实例的对话框,开发一些须要用户确认的功能就比较麻烦,其实完全可以加入模态窗口,简化开发。

模态对话框原理_模态对话框是什么时候实例的对话框_模态对话框的工作流程

注意事项

模态窗口极大地简化了一些须要和用户交互的操作,好处显而易见。但这儿还是要强调一些须要注意的地方,否则使用的时侯很可能会出问题。

影响PreTranslateMessage机制

在使用MFC,WTL等进行开发的时侯,经常用到PreTranslateMessage机制,这个机制可以让我们在消息被派发之前先做一些事情。很多人以为PreTranslateMessage是Windows本身支持的,其实不然。PreTranslateMessage是MFC和WTL自己引入的一个概念,完全是和Windows无关的。在MFC和WTL的消息循环中,这两个库的设计者在消息分发之前,人为的加了一些代码,使得整个构架支持这一套机制。

正是这般,如果在正常的流程中弹出了模态窗口,就会使正常的PreTranslateMessage机制失效。因为模态窗口中早已包含了一个消息循环,接管了线程中缺省的消息循环。而这个消息循环是在DialogBox这个API函数中执行的,显然不可能再有PreTranalateMessage机制了。

为了解决这一问题,只有让模态窗口也使用和UI线程相同的消息循环,MFC正是如此做的。在MFC中,对话框类的DoModal函数,并不是调用DialogBox函数,而是直接使用CreateWindows创建一个非模态窗口,在窗口创建成功以后再调用MFC自己的消息循环,这样就可以让PreTranslateMessage继续生效。同时在窗口创建下来以后,必须再做一些别的操作,使这个模态窗口的父窗口失效(一般直接把窗口Disable掉)。同时消息循环里有合适的退出条件,并有恢复现场的一些操作,具体可以查看MFC的DoModal函数。

WTL到目前为止,貌似暂时还没有一个合适的方案来解决这个问题。事实上WTL的PreTranslateMessage机制实现的虽然是有点问题的,或许之后会在这方面做一定的提高。

可能造成崩溃

这是一个严重问题,在条件合适的情况下,这个崩溃是必然的。

因为模态窗口弹下来以后,模态窗口旁边的代码在窗口关掉之前将不会得到执行。然而此时整个窗口是在正常运行的,对于一些极端的情况,是极有可能导致崩溃的。下面看一个事例:

void CTestDlg::OnOK()

CInputDialog dlg;

If(dlg.DoModal() == IDOK)

m_nValue = dlg.GetValue();iphone5

UpdateData(FALSE);

这是一段典型的MFC代码,在绝大多数情况下,不会有任何问题。但是因为模态窗口弹出的时侯,只是父窗口不能操作,但别的窗口完全能够正常运行,这时候就十分有可能因为某种缘由,CTestDlg类早已销毁了,而CInputDialog却不知道,还在继续执行,结果到了IDOK以后,对CTestDialog类的成员变量m_nValue形参,就会出现崩溃了。EMS

这个问题,如果在多线程的情况下,将会愈发严重。因为在多线程的情况下,将会有愈发多的不可预想的诱因,所以使用的时侯要愈发当心。

模态对话框是什么时候实例
上一篇:浏览器打不开网页?解决方法大揭秘 下一篇:没有了