为什么操作系统不直接调用回调函数?

时间:2021-02-25 14:28:53
本人小菜,最近正在学习MFC,看有讲到windows消息机制,里面讲到:WinMain()里面会有一个循环从操作系统的消息队列中获取消息,然后在调用DispatchMessage(&msg)告知操作系统调用回调函数WndProc()处理消息;

此处有一个比较幼稚的疑问, 为什么操作系统不直接调用回调函数?
为什么在消息发生时,操作系统不直接调用窗口的回调函数WndProc(),而要在应用程序WinMain()中获取消息->再告知操作系统调用回调函数?

为什么不设计成这样:当消息或者事件发生时,操作系统直接调用某个窗口的回调函数,将消息作为参数传递给该回调函数处理呢?为什要WinMain()先用GetMessage()获取消息,再用DispatchMessage(&msg)通知操作系统调用自己的窗口函数呢?这样一来控制权岂不是由窗口发起,递交给操作系统,再递交给窗口(回调函数),绕了一圈?

请高手详细解释一下,感谢!

23 个解决方案

#1


如果用户想修改窗口函数怎么办,按你说的情况。

#2


引用 1 楼 jerry_dqh 的回复:
如果用户想修改窗口函数怎么办,按你说的情况。


修改窗口函数?你是说WndProc()?
可以改啊

我想问的意思是窗口函数还是自己写,自己修改都行,但是由操作系统直接调用,不必由应用程序使用DispatchMessage()通知操作系统调用窗口函数;

#3


网上看到一些解释,如下:

“==================================================
其实问题的关键在于DispatchMessage到底干了什么 
如果只是去调用相应的窗口,那自己写个switch不就可以了。DispatchMessage与switch不同之处在于DispatchMessage会先调用windows,进入管态(大概是range 0),然后再由windows调用窗口的函数。 为什么这么麻烦? 因为这样windows就可以知道你的程序运行到什么情况了, windows来调用你的窗口,这样你的窗口返回的时候windows就知道你已经处理过一个消息了,如果没有新的消息进入消息队列windows就不再会给你的进程分配时间片如果是你自己写switch的话,windows就不可能这样灵活的分配时间资源利用率就会降低;

那么还要消息循环干什么,windows直接把消息发给窗口不就可以了吗?因为你要在消息循环里把KEY_DOWN和KEY_UP组合成WM_CHAR,还可以直接屏蔽掉许多对你来说无用的消息,加快速度。
==================================================”

难道就是因为这些?没有其他原因么?求解释!!!
为什么操作系统不直接调用回调函数?

#4


直接调用就是SendMessage类似处理的,但是还有异步的方式PostMessage

#5


不是系统调用的WNDPROC,系统只负责通知和调试,不参与回调

#6


如果系统调用所有WNDPROC那其中一个过程有长时间耗时运算岂不是整个系统都无法响应?

while (GetMessage(&msg, NULL, 0, 0))
{
    TraslateMessage(&msg);
    DispatchMessage(&msg);
}

WNDPROC ...

典型的消息泵,系统参与工作的就只有GetMessage 这句,从系统那取到消息。然后DispatchMessage再分发消息到同一线程的各WNDPROC,真正是由DispatchMessage回调WndProc的。所以DispatchMessage在哪个线程,就相当于谁执行了WndProc。

#7


该回复于2013-07-27 16:52:31被管理员删除

#8


用消息来实现模块封装
例如,有甲乙丙丁四个程序,各有不同的鼠标单击处理方法,有的需要一个参数有的需要两个参数有的不需要参数,希望这四个程序都处理一次鼠标单击,如果用回调函数,那么负责这件事的程序就必须知道这四个程序的具体函数地址与格式,如果用消息,只需要发四条通用格式的消息并注明每个消息的目标窗口,不需要知道各程序的具体细节
虽然情况有各种各样,但基本思想都是类似的,发送消息和执行消息的两部分(两个函数或线程或窗口进程等等)彼此互相不需要知道对方是什么,这就是消息机制最大的优点

#9


楼主大概是正处在语法学习的差不多了,但还没有了解各种编程思想的阶段吧
建议学习一下各种设计模式,哪怕暂时用不上起码先了解一下,你会看到有很多地方需要在程序代码上绕弯来实现程序整体的易维护易扩展

#10


LZ的问题乍看起来容易让人产生误解,仔细看过之后,明白了LZ的问题。
我认为:Windows设计消息管理机制时,希望用面向对象的思想对消息进行封装(不是用面向对象的C++语法进行封装),使用的是不支持面向对象表达的C语言WIN16/WIN32 API接口。

封装后的对开发者提出的强制要求就是:窗口类、窗口回调WNDPROC、消息循环GetMessage()处理Send的消息、消息派遣DispatchMessage()处理Post的消息。大家都按照这个标准来开发Windows程序。

我想这也就是9楼说的一种设计思想。

#11


while (GetMessage(&msg, NULL, 0, 0)) { 
    TraslateMessage(&msg);     
    DispatchMessage(&msg); 

这段代码保证了窗口过程,在用户程序的主线程里运行,这样对系统窗口过程的调用,才能保证在用户程序的地址空间和时间片里运行,如果系统自己调用窗口过程;
那么
1)用户程序,就没有办法控制程序的运行了。
2)程序占用了系统的时间片了。
3)用户地址空间和系统地址空间,没有办法分割了。
4)系统没有办法管理,用户程序的运行了。
 GetMessage这个动作保证了,用户程序的消息不会堆积下来。
 TraslateMessage(&msg);     
 DispatchMessage(&msg); 
保证用户程序,能够正确处理窗口的消息。
有了这样一个消息循环,窗口程序就能不断的处理消息,保证用户程序的正确运行了。

 

#12


想象一下HOOK

#13


为什么操作系统不直接调用回调函数?
大家都很犀利!首先感谢各位大神的回答!让我慢慢阅读理解各位的回答!

#14


引用 4 楼 oyljerry 的回复:
直接调用就是SendMessage类似处理的,但是还有异步的方式PostMessage


我不理解你说的SendMessage/PostMessage是在什么场景下应用的?
你是说GetMessage/PeekMessage么?
能否详细讲解一下?

#15


引用 5 楼 bllqbz 的回复:
不是系统调用的WNDPROC,系统只负责通知和调试,不参与回调


==================================================
DispatchMessage函数功能:该函数分发一个消息给窗口程序。通常消息从GetMessage函数获得。消息被分发到回调函数(过程函数),作用是消息传递给操作系统, 然后操作系统去调用我们的回调函数,也就是说我们在窗体的过程函数中处理消息
==================================================

这是度娘的原话,WinMain用DispatchMessage函数将消息传递给系统,操作系统调用回调函数!
你说不是系统调用WNDPROC?这我就迷糊了?还望详细解释!

#16


回调回调!!!顾名思义啊

#17


引用 8 楼 baichi4141 的回复:
用消息来实现模块封装
例如,有甲乙丙丁四个程序,各有不同的鼠标单击处理方法,有的需要一个参数有的需要两个参数有的不需要参数,希望这四个程序都处理一次鼠标单击,如果用回调函数,那么负责这件事的程序就必须知道这四个程序的具体函数地址与格式,如果用消息,只需要发四条通用格式的消息并注明每个消息的目标窗口,不需要知道各程序的具体细节
虽然情况有各种各样,但基本思想都是类似的,发送消息和执行消息的两部分(两个函数或线程或窗口进程等等)彼此互相不需要知道对方是什么,这就是消息机制最大的优点


没懂!我觉得你说的“发送消息和执行消息的两部分(两个函数或线程或窗口进程等等)彼此互相不需要知道对方是什么,这就是消息机制最大的优点”,这个我没有疑问,WinMain与WndProc是分开的,但是为什么消息发生时操作系统不直接调用WndProc?

#18


引用 11 楼 lm_whales 的回复:
while (GetMessage(&msg, NULL, 0, 0)) { 
    TraslateMessage(&msg);     
    DispatchMessage(&msg); 

这段代码保证了窗口过程,在用户程序的主线程里运行,这样对系统窗口过程的调用,才能保证在用户程序的地址空间和时间片里运行,如果系统自己调用窗口过程;
那么
1)用户程序,就没有办法控制程序的运行了。
2)程序占用了系统的时间片了。
3)用户地址空间和系统地址空间,没有办法分割了。
4)系统没有办法管理,用户程序的运行了。
 GetMessage这个动作保证了,用户程序的消息不会堆积下来。
 TraslateMessage(&msg);     
 DispatchMessage(&msg); 
保证用户程序,能够正确处理窗口的消息。
有了这样一个消息循环,窗口程序就能不断的处理消息,保证用户程序的正确运行了。

 


针对你说的几点:
1)用户程序,就没有办法控制程序的运行了。——这个是说的WinMain中需要过滤一些消息这个控制动作?
2)程序占用了系统的时间片了。——那DispatchMessage函数将消息传递给系统,系统再去调用回调函数,这个动作不会占用系统的时间片段么?有什么区别?
3)这个更2)是一个道理吧?
4)这个也是由上面点造成的,暂且不谈把。

综合起来看,你的观点就是,如果系统直接调用WndProc,则WndProc运行在系统的地址空间;而由WinMain调用DispatchMessage函数通知系统调用WndProc,则WndProc运行在WinMain的地址空间;可以这么理解么?

但是为什么最后都是系统负责调用WndProc,到底有什么区别呢?

#19


引用 12 楼 VisualEleven 的回复:
想象一下HOOK


HOOK我就知道简单的挂钩原理,你的意思是说hook到消息后可以做一些处理、修改、过滤等等?

有两点疑问,为什么要让应用程序能够hook到消息?这样有什么好处?这个有点扯远了。

另外一个,WinMain与WndProc是同一个程序里,WinMain中能做的过滤、处理、修改等等,都可以放在WndProc中,感觉没必要在WinMain里获取消息,过滤、修改后再传给系统,让系统调用回调WndProc?
还望版主细讲!

#20


引用 16 楼 YI_RIVER_LOVE 的回复:
回调回调!!!顾名思义啊


我讲的也是由操作系统调用WndProc,没有与回调这个概念相悖吧?你强调回调什么意思呢?请细讲!

#21


DispatchMessage 这个函数的调用点在用户空间,所谓的系统调用,实际上是DispatchMessage这个函数在调用,只是DispatchMessage函数会进入系统内核,然后查找WndProc找到后返回用户地址空间和用户线程调用WndProc,这样就在用户地址空间,调用了WndProc,在用户线程的时间片里运行WndProc了。
操作系统,只是为用户程序提供服务的,用户程序,才是真正完成工作的程序。

操作系统,主要做以下工作。
1)管理用户程序。
2)为用户程序提供服务。

具体执行的任务,实际上都是用户程序提供的。
像画图,NotePad这样的程序,实际上,是操作系统附带提供的,用户程序。
真正的系统内核,并不包含这些程序。
Cmd,explorer 则处于二者之间,是操作系统的外壳程序,实际上,用户程序也可以实现类似的功能。比如unix的,linux的各种shell。

Windows 系统,各种驱动程序,处于系统内核位置,GDI,USER,Kenerl 三大模块,是系统的核心模块。
各种API,都可能有机会进入系统内核,从而唤醒内核调度程序。DispatchMessage是处理消息的核心API之一。如果应用程序不调用,这些API,没有办法在核心和用户状态之间转换。
系统直接调用的话,那就处于内核的地址空间,而不是用户的地址空间了。

同样,也不处于用户的时间片了,用户线程,无法运行,这样系统程序到处跑,用户程序饿死,这个系统是无法正常使用的。

系统是用来管理,维护用户程序,并为用户程序提供服务的,不能全速运行。

系统程序,应该占用尽量少的时间,从而让用户程序,可以流畅的运行。




#22


Windows 回调程序的概念是,应用程序调用API,API 调用回调函数。

这就是回调函数的含义。

具体实现,就是注册或者传入一个函数指针。

这个函数由用户程序实现。注册或者传入的是用户实现的那个函数的地址。

函数格式---函数参数的个数和类型,返回值的类型,函数调用约定---由系统指定,而不是,用户随意定义。

WndProc 由RegisterClass, RegisterClassEx等 函数注册,DispatchMessage等函数最终调用

这就是 Windows 消息循环存在的理由。

 

#23


我的理解是:操作系统中会同时开启多个应用程序,操作系统的设计者不会把消息处理的过程完全交给应用程序去处理。否则,这样的操作系统的调度功能就会很弱,实时响应也很差。所以一定要让操作系统尽量介入细节中,才能进行协调工作。类似机场空管人员会时刻了解每架飞机的位置信息,不会让每架飞机自己决定起降时间和场次。你能想象每架飞机自己决定起降时间吗,这个机场就乱套了。把每个应用程序看成一架飞机就好理解了。

#1


如果用户想修改窗口函数怎么办,按你说的情况。

#2


引用 1 楼 jerry_dqh 的回复:
如果用户想修改窗口函数怎么办,按你说的情况。


修改窗口函数?你是说WndProc()?
可以改啊

我想问的意思是窗口函数还是自己写,自己修改都行,但是由操作系统直接调用,不必由应用程序使用DispatchMessage()通知操作系统调用窗口函数;

#3


网上看到一些解释,如下:

“==================================================
其实问题的关键在于DispatchMessage到底干了什么 
如果只是去调用相应的窗口,那自己写个switch不就可以了。DispatchMessage与switch不同之处在于DispatchMessage会先调用windows,进入管态(大概是range 0),然后再由windows调用窗口的函数。 为什么这么麻烦? 因为这样windows就可以知道你的程序运行到什么情况了, windows来调用你的窗口,这样你的窗口返回的时候windows就知道你已经处理过一个消息了,如果没有新的消息进入消息队列windows就不再会给你的进程分配时间片如果是你自己写switch的话,windows就不可能这样灵活的分配时间资源利用率就会降低;

那么还要消息循环干什么,windows直接把消息发给窗口不就可以了吗?因为你要在消息循环里把KEY_DOWN和KEY_UP组合成WM_CHAR,还可以直接屏蔽掉许多对你来说无用的消息,加快速度。
==================================================”

难道就是因为这些?没有其他原因么?求解释!!!
为什么操作系统不直接调用回调函数?

#4


直接调用就是SendMessage类似处理的,但是还有异步的方式PostMessage

#5


不是系统调用的WNDPROC,系统只负责通知和调试,不参与回调

#6


如果系统调用所有WNDPROC那其中一个过程有长时间耗时运算岂不是整个系统都无法响应?

while (GetMessage(&msg, NULL, 0, 0))
{
    TraslateMessage(&msg);
    DispatchMessage(&msg);
}

WNDPROC ...

典型的消息泵,系统参与工作的就只有GetMessage 这句,从系统那取到消息。然后DispatchMessage再分发消息到同一线程的各WNDPROC,真正是由DispatchMessage回调WndProc的。所以DispatchMessage在哪个线程,就相当于谁执行了WndProc。

#7


该回复于2013-07-27 16:52:31被管理员删除

#8


用消息来实现模块封装
例如,有甲乙丙丁四个程序,各有不同的鼠标单击处理方法,有的需要一个参数有的需要两个参数有的不需要参数,希望这四个程序都处理一次鼠标单击,如果用回调函数,那么负责这件事的程序就必须知道这四个程序的具体函数地址与格式,如果用消息,只需要发四条通用格式的消息并注明每个消息的目标窗口,不需要知道各程序的具体细节
虽然情况有各种各样,但基本思想都是类似的,发送消息和执行消息的两部分(两个函数或线程或窗口进程等等)彼此互相不需要知道对方是什么,这就是消息机制最大的优点

#9


楼主大概是正处在语法学习的差不多了,但还没有了解各种编程思想的阶段吧
建议学习一下各种设计模式,哪怕暂时用不上起码先了解一下,你会看到有很多地方需要在程序代码上绕弯来实现程序整体的易维护易扩展

#10


LZ的问题乍看起来容易让人产生误解,仔细看过之后,明白了LZ的问题。
我认为:Windows设计消息管理机制时,希望用面向对象的思想对消息进行封装(不是用面向对象的C++语法进行封装),使用的是不支持面向对象表达的C语言WIN16/WIN32 API接口。

封装后的对开发者提出的强制要求就是:窗口类、窗口回调WNDPROC、消息循环GetMessage()处理Send的消息、消息派遣DispatchMessage()处理Post的消息。大家都按照这个标准来开发Windows程序。

我想这也就是9楼说的一种设计思想。

#11


while (GetMessage(&msg, NULL, 0, 0)) { 
    TraslateMessage(&msg);     
    DispatchMessage(&msg); 

这段代码保证了窗口过程,在用户程序的主线程里运行,这样对系统窗口过程的调用,才能保证在用户程序的地址空间和时间片里运行,如果系统自己调用窗口过程;
那么
1)用户程序,就没有办法控制程序的运行了。
2)程序占用了系统的时间片了。
3)用户地址空间和系统地址空间,没有办法分割了。
4)系统没有办法管理,用户程序的运行了。
 GetMessage这个动作保证了,用户程序的消息不会堆积下来。
 TraslateMessage(&msg);     
 DispatchMessage(&msg); 
保证用户程序,能够正确处理窗口的消息。
有了这样一个消息循环,窗口程序就能不断的处理消息,保证用户程序的正确运行了。

 

#12


想象一下HOOK

#13


为什么操作系统不直接调用回调函数?
大家都很犀利!首先感谢各位大神的回答!让我慢慢阅读理解各位的回答!

#14


引用 4 楼 oyljerry 的回复:
直接调用就是SendMessage类似处理的,但是还有异步的方式PostMessage


我不理解你说的SendMessage/PostMessage是在什么场景下应用的?
你是说GetMessage/PeekMessage么?
能否详细讲解一下?

#15


引用 5 楼 bllqbz 的回复:
不是系统调用的WNDPROC,系统只负责通知和调试,不参与回调


==================================================
DispatchMessage函数功能:该函数分发一个消息给窗口程序。通常消息从GetMessage函数获得。消息被分发到回调函数(过程函数),作用是消息传递给操作系统, 然后操作系统去调用我们的回调函数,也就是说我们在窗体的过程函数中处理消息
==================================================

这是度娘的原话,WinMain用DispatchMessage函数将消息传递给系统,操作系统调用回调函数!
你说不是系统调用WNDPROC?这我就迷糊了?还望详细解释!

#16


回调回调!!!顾名思义啊

#17


引用 8 楼 baichi4141 的回复:
用消息来实现模块封装
例如,有甲乙丙丁四个程序,各有不同的鼠标单击处理方法,有的需要一个参数有的需要两个参数有的不需要参数,希望这四个程序都处理一次鼠标单击,如果用回调函数,那么负责这件事的程序就必须知道这四个程序的具体函数地址与格式,如果用消息,只需要发四条通用格式的消息并注明每个消息的目标窗口,不需要知道各程序的具体细节
虽然情况有各种各样,但基本思想都是类似的,发送消息和执行消息的两部分(两个函数或线程或窗口进程等等)彼此互相不需要知道对方是什么,这就是消息机制最大的优点


没懂!我觉得你说的“发送消息和执行消息的两部分(两个函数或线程或窗口进程等等)彼此互相不需要知道对方是什么,这就是消息机制最大的优点”,这个我没有疑问,WinMain与WndProc是分开的,但是为什么消息发生时操作系统不直接调用WndProc?

#18


引用 11 楼 lm_whales 的回复:
while (GetMessage(&msg, NULL, 0, 0)) { 
    TraslateMessage(&msg);     
    DispatchMessage(&msg); 

这段代码保证了窗口过程,在用户程序的主线程里运行,这样对系统窗口过程的调用,才能保证在用户程序的地址空间和时间片里运行,如果系统自己调用窗口过程;
那么
1)用户程序,就没有办法控制程序的运行了。
2)程序占用了系统的时间片了。
3)用户地址空间和系统地址空间,没有办法分割了。
4)系统没有办法管理,用户程序的运行了。
 GetMessage这个动作保证了,用户程序的消息不会堆积下来。
 TraslateMessage(&msg);     
 DispatchMessage(&msg); 
保证用户程序,能够正确处理窗口的消息。
有了这样一个消息循环,窗口程序就能不断的处理消息,保证用户程序的正确运行了。

 


针对你说的几点:
1)用户程序,就没有办法控制程序的运行了。——这个是说的WinMain中需要过滤一些消息这个控制动作?
2)程序占用了系统的时间片了。——那DispatchMessage函数将消息传递给系统,系统再去调用回调函数,这个动作不会占用系统的时间片段么?有什么区别?
3)这个更2)是一个道理吧?
4)这个也是由上面点造成的,暂且不谈把。

综合起来看,你的观点就是,如果系统直接调用WndProc,则WndProc运行在系统的地址空间;而由WinMain调用DispatchMessage函数通知系统调用WndProc,则WndProc运行在WinMain的地址空间;可以这么理解么?

但是为什么最后都是系统负责调用WndProc,到底有什么区别呢?

#19


引用 12 楼 VisualEleven 的回复:
想象一下HOOK


HOOK我就知道简单的挂钩原理,你的意思是说hook到消息后可以做一些处理、修改、过滤等等?

有两点疑问,为什么要让应用程序能够hook到消息?这样有什么好处?这个有点扯远了。

另外一个,WinMain与WndProc是同一个程序里,WinMain中能做的过滤、处理、修改等等,都可以放在WndProc中,感觉没必要在WinMain里获取消息,过滤、修改后再传给系统,让系统调用回调WndProc?
还望版主细讲!

#20


引用 16 楼 YI_RIVER_LOVE 的回复:
回调回调!!!顾名思义啊


我讲的也是由操作系统调用WndProc,没有与回调这个概念相悖吧?你强调回调什么意思呢?请细讲!

#21


DispatchMessage 这个函数的调用点在用户空间,所谓的系统调用,实际上是DispatchMessage这个函数在调用,只是DispatchMessage函数会进入系统内核,然后查找WndProc找到后返回用户地址空间和用户线程调用WndProc,这样就在用户地址空间,调用了WndProc,在用户线程的时间片里运行WndProc了。
操作系统,只是为用户程序提供服务的,用户程序,才是真正完成工作的程序。

操作系统,主要做以下工作。
1)管理用户程序。
2)为用户程序提供服务。

具体执行的任务,实际上都是用户程序提供的。
像画图,NotePad这样的程序,实际上,是操作系统附带提供的,用户程序。
真正的系统内核,并不包含这些程序。
Cmd,explorer 则处于二者之间,是操作系统的外壳程序,实际上,用户程序也可以实现类似的功能。比如unix的,linux的各种shell。

Windows 系统,各种驱动程序,处于系统内核位置,GDI,USER,Kenerl 三大模块,是系统的核心模块。
各种API,都可能有机会进入系统内核,从而唤醒内核调度程序。DispatchMessage是处理消息的核心API之一。如果应用程序不调用,这些API,没有办法在核心和用户状态之间转换。
系统直接调用的话,那就处于内核的地址空间,而不是用户的地址空间了。

同样,也不处于用户的时间片了,用户线程,无法运行,这样系统程序到处跑,用户程序饿死,这个系统是无法正常使用的。

系统是用来管理,维护用户程序,并为用户程序提供服务的,不能全速运行。

系统程序,应该占用尽量少的时间,从而让用户程序,可以流畅的运行。




#22


Windows 回调程序的概念是,应用程序调用API,API 调用回调函数。

这就是回调函数的含义。

具体实现,就是注册或者传入一个函数指针。

这个函数由用户程序实现。注册或者传入的是用户实现的那个函数的地址。

函数格式---函数参数的个数和类型,返回值的类型,函数调用约定---由系统指定,而不是,用户随意定义。

WndProc 由RegisterClass, RegisterClassEx等 函数注册,DispatchMessage等函数最终调用

这就是 Windows 消息循环存在的理由。

 

#23


我的理解是:操作系统中会同时开启多个应用程序,操作系统的设计者不会把消息处理的过程完全交给应用程序去处理。否则,这样的操作系统的调度功能就会很弱,实时响应也很差。所以一定要让操作系统尽量介入细节中,才能进行协调工作。类似机场空管人员会时刻了解每架飞机的位置信息,不会让每架飞机自己决定起降时间和场次。你能想象每架飞机自己决定起降时间吗,这个机场就乱套了。把每个应用程序看成一架飞机就好理解了。