模态对话框的消息循环原理及分析笔记

2023-05-16

简述: APP消息循环和模态对话框中局部消息循环的关系

根据上图可以看出,在APP的消息循环再派发ONOK消息后,调用ModalDlg的响应函数,pWnd->OnOk();在该消息中,
会 进入模态对话框的消息循环,除非将模态对话框关闭,否则APP的DispatchMessage函数一直出不来。

一旦创建了模态对话框,进行局部消息循环,那么APP的消息循环就被阻断。整个程序的消息循环有模态对话框中得消息循环取代。所以给父窗口发送的非窗口消息,一样可以响应。

由于局部消息循环只在对话框中的一个响应函数中,而全局的消息循环也被阻断,局部循环一直运行,如果用户不进行处理并关闭模态对话框,该循环会一直不退出。其他对话框也得不到处理。

1.模态对话框有自己的消息循环,而非模态对话框没有自己的消息循环,所以在异步线程里创建了一个非模态的窗体此窗体会一闪而过(无消息循环不能处理各种消息事件),而在本线程里创建则是共享本线程的消息循环不会闪退。2.本线程里创建模态对话框A后,A的消息循环活动而本线程(主UI线程)被阻塞。

2.模态对话框在创建后,主程序的的消息循环是空的(除非对话框给主程序发送消息就另当别论了),就是与系统消息循环断开联系了,而进入了自己独有的一个消息循环。 然而非模态对话框在创建后立即返回,并且和主程序共用一个消息循环。

3.可以直接调用消息响应过程函数,即发送“::SendMessage(新对话框的hWnd,WM_CLOSE,0,0);”来调用消息响应过程函数来关闭新对话框。

4.1当通过ShowDialog方法来显示一个模态窗体时:调用该窗体的Close方法后,只是隐藏了该窗体,仍然可以访问该窗体,并可以取得该窗体上控件的值,而且还可以通过ShowDialog方法重新显示该窗体。当调用该窗体的Dispose方法时,等于释放了该窗体所占有的资源,但该对象任然存在,可以访问该窗体,也可以取得窗体上控件的值,但是不能再通过ShowDialog方法重新显示该窗体。

4.2当通过Show方法来显示一个非模态窗体时,情况就简单许多,因为Close方法和Dispose方法是一样的,他们都调用了带一个参数的Dispose方法。所以调用模态对话框的close后关闭不了窗体需要Dispose才能关闭。

5.1本线程里创建模态对话框A后,A的消息循环活动而本线程(主UI线程)被阻塞。当在主线程里有一个方法包含A的close时,由于close实质调用的也是一种消息,该消息并不会释放A的资源(只是隐藏窗体而已),所以如果此时用close可以关掉模态对话框A,但是如果再本线程里又创建了一个模态对话框B,再A.close()时则不会关掉A,因为A只是隐藏了,此时只有B的消息循环处于活动中,B的消息循环里依然可以分配各个窗口的消息队列并分发给相应窗体来调用消息响应过程函数,所以A仍然可以使隐藏的A显示并处理部分消息事件。

5.2异步线程里创建模态窗体后能被close,是因为异步线程也随着窗体的close关闭了,自动调用了dispose等释放资源。2.而本线程创建的模态窗体(假如此时在主UI窗体线程里又依次创建了模态窗体A和模态窗体B,先通过句柄查找窗体然后调用A.close,这时关闭不了。当调用B.close时能关闭,然后再调用A.close才有效)由于共享本线程里被激活的活动的UI窗体的消息循环,虽然close(实质上模态窗体的close是隐藏并不调用销毁释放资源等函数,非模态的close可以释放资源,注意:非模态窗体没有自己的消息循环)了但是还能有显示等消息事件的处理,因为同一个线程的唯一的活动的消息循环都是共享的,每个窗体都有自己的消息循环,但是活动的消息循环只有一个,其他的窗体的消息循环被后来创建的模态窗体阻塞,而最后创建的模态窗体的消息循环进入活动状态,相当于while循环里不断套用新的while,即消息循环的嵌套。注意:sendmessage是直接调用窗体的消息响应过程函数,模态窗体的showdialog并不是阻塞线程,只是阻塞了父窗体即其他模态窗体的消息循环,与sleep不同,故虽然同一线程里有很多模态窗体并showdialog,但是这些窗体都能被间接通过sendmessage调用,或通过查找句柄的方式来调用某些窗体的操作(这时线程不是阻塞的,只是依次的上级消息循环被阻塞,但是活动的消息循环是各个模态窗体共享的,所以其他的模态窗体人盎然能够接收处理部分消息和事件,只是某些系统的消息事件如按键鼠标等被屏蔽了,这些系统消息只有在活动的消息循环的模态窗体里采能被有效处理这时windows机制)。

6.实例验证


Task.Factory.StartNew(() => {
                    Frm frm = new Frm();  
                    Task.Factory.StartNew(() => {
                        Thread.Sleep(5000);
                        frm.Invoke(new Action(() => { MessageBox.Show("Box"); }));
                    });
                    frm.ShowDialog();
                });如此线程创建的窗体,即使被别的地方根据句柄查找控件把frm.close了,也还能显示,因为messagebox对话框有自己的消息循环,可以让隐藏的frm显示和处理各种消息,除非不弹messagebox,这样此线程就没有活动的消息循环了自然可以退出线程释放frm(分析:5秒后messagebox的消息循环被激活,frm的消息循环被阻塞,这个线程的frm和messagebox共用messagebox的消息循环)。  

7.只有活动的消息循环的窗体再执行close时才会有效,因为close相关的消息在阻塞的消息循环的窗体不会被执行,所以如果同一个线程里有多个模态窗体,且被close的窗体的消息循环被阻塞的话,此窗体虽然能共享活动的消息循环的窗体但是部分系统消息控件消息及事件会屏蔽(因为只有活动的消息循环的窗体才能完全接收和处理各种消息事件,其他窗体重入共享消息循环的窗体只能处理部分如重绘等消息事件),所以才会出现close不掉的情况。消息循环嵌套阻塞,类似于while循环阻塞。

8.当使用SendMessge发送相关消息给被阻塞的消息循环的窗体时应该都是可以有效的,因为是直接将消息发送到本线程中共享的消息队列中去了。而通过句柄查找窗体直接调用close的方式并不是发送的消息。

MFC中关闭窗口的几种办法:

退出程序用AfxGetMainWnd()->SendMessage(WM_CLOSE);

关闭当前窗口用DestroyWindow( );

关闭模式对话框用EndDialog(0);

9.MFC实例解释如下所示

一个典型的Win32项目(不是MFC项目,只有一个窗口的项目),其中的消息循环会使用如下代码实现:


//代码段1

MSG msg
BOOL bRet;

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)

    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    }
}

GetMessage()是取得消息,如果没有消息,线程阻塞在这里,不占用CPU。

DispatchMessage()是将消息分发,分发到所有在这个线程中创建的窗口的窗口处理函数中去。 如果不需要分发消息,就不需要调用DispatchMessage(),例如线程中没有窗口的情况。
线程消息循环:

实际上,任何线程只要调用了上述代码段1中的代码,就已经实现了消息循环的功能。更进一步,其实只要调用GetMessage()函数就可以了。

即:

//代码段2

BOOL bRet;  MSG msg;
while( (bRet = GetMessage(&msg, NULL, 0, 0)) != 0)

if (bRet == -1 || WM_QUIT == msg.message){
// handle the error and possibly exit
break;
}
        }

在工作线程中,只要有代码段2,就可以实现消息循环的功能了。

这是一个很方便的线程间异步通信的机制。给线程发消息用PostThreadMessage()。为什么去掉了DispatchMessage()呢,因为一般情况下,在工作线程中是不需要创建窗口的,不需要分发到窗口中去处理,能从线程的消息队列里取得消息就足够了。

如果设计线程的初衷为:当某条件发生时,让工作线程开始工作,一直将工作完成,之后挂起,等待新条件的发生,等待时不占用CPU资源。 那么代码段2所展示的机制就能很好的完成这样的功能。 而且可以使多个工作线程都用同样的机制实现,彼此间协同工作,加之某种资源共享机制,可以实现一个异步的消息处理链。
模态对话框:

消息循环可以有多个,可以在上一级消息循环的某个消息的处理过程中,局部创建一个消息循环,模态对话框就是采用这种机制创建出来的。

一个线程可以有多个消息循环,并行的消息循环显然没有意义,多个就是在消息循环中嵌套消息循环。

如代码段1中的DispatchMessage函数,将消息派发到窗口的消息处理函数中,WndProc1()。

现在假设在WndProc1()中创建一个消息循环,并且只取得这个窗口本身的消息 GetMessage(&msg, hWnd, 0, 0),不必Dispatch了,因为已经找到了目标窗口,然后用这个窗口真正的消息处理函数去处理。 这样模态对话框存在时,处理了所有的窗口消息,创建模态对话框的窗口就一直得不到处理消息的机会,模块对话框就始终处于最上层,直到其主动退出。

当然,对模态对话框的这种解释是简化了的,实际过程可能复杂的多,取得的窗口消息至少要包含所有的子窗口的消息,也需要Dispatch到相应的子窗口,等等。但是最基本的机制应该就是这样的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

模态对话框的消息循环原理及分析笔记 的相关文章

随机推荐

  • c语言中函数调用的原理及形参传递实质原理分析

    一 函数参数传递机制的基本理论 函数参数传递机制问题在本质上是调用函数 xff08 过程 xff09 和被调用函数 xff08 过程 xff09 在调用发生时进行通信的方法问题 基本的参数传递机制有两种 xff1a 值传递和引用传递 以下讨
  • c++中多线程传递参数原理分析

    线程可以共享进程的内存空间 xff0c 线程拥有自己独立内存 关于参数的传递 xff0c std thread的构造函数只会单纯的复制传入的变量 xff0c 特别需要注意的是传递引用时 xff0c 传入的是值的副本 xff0c 也就是说子线
  • 线程的局部变量ThreadLocal概念

    ThreadLocal是什么 对这个词语分解 xff0c 将其分为Thread和Local xff0c 顾名思义便是本线程的变量 xff0c 既然是当前线程的变量 xff0c 那么就意味着这个变量对于其他线程来说就是隔离的 xff0c 也就
  • C++多线程并发中线程管理

    一 何为并发 刚开始接触计算机编程语言时 xff0c 我们编写一个程序 xff0c 在main入口函数中调用其它的函数 xff0c 计算机按我们设定的调用逻辑来执行指令获得结果 如果我们想在程序中完成多个任务 xff0c 可以将每个任务实现
  • 多线程中堆和栈区别的深入解析

    很多现代操作系统中 xff0c 一个进程的 xff08 虚 xff09 地址空间大小为4G xff0c 分为系统空间和用户空间两部分 xff0c 系统空间为所有进程共享 xff0c 而用户空间是独立的 xff0c 一般WINDOWS进程的用
  • C语言中队列、堆栈、内存映射、多线程概念

    队列 xff1a 先近先出 xff1b 栈 xff1a 先近后出 xff1b 栈的大小是由编译器决定的 xff0c 默认大小是1M xff0c 可以更改 xff0c 但是一般不建议修改 xff0c 每个exe都有一个栈 xff0c 无法利用
  • C++多线程编程分析-线程间通信

    上文我们介绍了如何建立一个简单的多线程程序 xff0c 多线程之间不可避免的需要进行通信 相比于进程间通信来说 xff0c 线程间通信无疑是相对比较简单的 首先我们来看看最简单的方法 xff0c 那就是使用全局变量 xff08 静态变量也可
  • 多线程访问全局变量和局部变量剖析

    如果一个变量是成员变量 xff0c 那么多个线程对同一个对象的成员变量进行操作时 xff0c 它们对该成员变量是彼此影响的 xff0c 也就是说一个线程对成员变量的改变会影响到另一个线程 如果一个变量是局部变量 xff0c 那么每个线程都会
  • c语言中全局变量多线程调用-局部变量、静态局部变量、全局变量与静态全局变量分析

    基本概念 xff1a 作用域 xff1a 起作用的区域 xff0c 也就是可以工作的范围 代码块 xff1a 所谓代码块 xff0c 就是用 括起来的一段代码 数据段 xff1a 数据段存的是数 xff0c 像全局变量就是存在数据段的 代码
  • vnc viewer 绿色版,6款超好用的vnc viewer 绿色版

    市面上形形色色的vnc viewer 绿色版软件很多 在众多的vnc viewer 绿色版软件中 你会选择哪一款呢 你所了解的vnc viewer 绿色版又有哪些呢 接下来让我们一起看看有哪些好用的vnc viewer 绿色版软件吧 第一款
  • django 框架模型之models常用的Field,这些Field的参数、及常见错误原因及处理方案。

    1 django 模型models 常用字段 1 models AutoField 自增列 61 int 11 如果没有的话 xff0c 默认会生成一个名称为 id 的列 如果要显式的自定义一个自增列 xff0c 必须设置primary k
  • 干货丨ChatGPT大爆发以来,最值得收藏的30个AI工具,让你生产力爆表、效率无敌!

    随着ChatGPT的火爆出圈 xff0c 炸出来一堆 AI神器 xff0c 它们不仅大大拓宽了我们原本的能力范围 xff0c 更是让工作效率瞬间翻倍 接下来 xff0c 给大家推荐30个精选的 AI工具 xff0c 拿走 xff0c 直接用
  • 多线程下局部变量与全局变量的使用及区别

    局部变量是在栈中运行 每个运行的线程都有自己的堆栈 别的线程无法访问得到 xff0c 因此我们说 xff0c 局部变量是 安全 的 全局变量是在堆中运行 堆是对所有的线程都可见的 因此在两个以上的线程访问全局变量时 xff0c 就会出现所谓
  • MFC中Windows窗口消息循环及多线程之间关系

    Windows中一个进程可以包含多个线程 xff0c 由多个线程组成 在Windows应用程序中 xff0c 窗体是由 UI线程 xff08 User Interface Thread xff09 的特殊类型的线程创建的 一个UI线程包含一
  • c#中Show和Showdialog的区别分析

    简单地说他们的区别就是show弹出来的窗体和父窗体 xff08 上一个窗体的简称 xff09 是属于同一等级的 xff0c 这两个窗体可以同时存在而且可以随意切换 xff0c 但是showdialog弹出来的窗体就不能这样 xff0c 他永
  • 模态对话框和非模态对话框的消息循环分析

    1 非模态对话框和父窗口共享当前线程的消息循环 2 模态对话框新建一个新的消息循环 xff0c 并由当前消息循环派发消息 xff0c 而父窗口 模态对话框屏蔽了用户对它父窗口的操作 xff0c 但是不是在消息循环里面屏蔽 xff0c 所以给
  • MFC中实现模态对话框的结构与原理

    1 模态对话框 在涉及GUI程序开发的过程中 xff0c 常常有模态对话框以及非模态对话框的概念 模态对话框 xff1a 在子界面活动期间 xff0c 父窗口是无法进行消息响应 独占用户输入 非模态对话框 xff1a 各窗口之间不影响 模态
  • 深入理解MFC消息循环和消息泵的原理

    首先 xff0c 应该清楚MFC的消息循环 GetMessage PeekMessage xff0c 消息泵 CWinThread PumpMessage 和MFC的消息在窗口之间的路由是两件不同的事情 在MFC的应用程序中 应用程序类基于
  • 窗口结束后资源释放不掉问题解决办法

    net类库已经帮助我们实现了窗口的关闭 xff0c 如果此窗口是系统的主窗口 xff0c 关闭此窗口即应该退出了整个应用程序 但事实上有时候并不是这样的 xff0c 关闭窗口 xff0c 只是停止了当前窗口的消息循环 系统主窗口 xff0c
  • 模态对话框的消息循环原理及分析笔记

    简述 xff1a APP消息循环和模态对话框中局部消息循环的关系 根据上图可以看出 xff0c 在APP的消息循环再派发ONOK消息后 xff0c 调用ModalDlg的响应函数 xff0c pWnd gt OnOk 在该消息中 xff0c