Windows消息处理 BCB
本文研究了BCB中的消息处理机制,在此基础上提出了处理Windows消息和自定义消息响应的方法和建立动态和静态消息映射的技巧。

  C++ Builder作为一种RAD方式的程序开发工具,提供了功能强大的集成开发环境。C++ Builder提供的VCL组件,封装了Windows的底层API和具体实现细节,也提供了对组件消息机制的良好封装。比如,可以对按钮控件(TButton)的单击、按下、拖动等事件消息设置相应的处理函数,并在对应的函数体中实现对该消息的处理与响应。虽然C++ Builder对VCL组件的消息处理提供了一套良好的处理机制,且封装了许多常用的消息,但是当开发者需要处理未定义的Windows消息或自定义消息时,C++ Builder不能提供直接的支持。这就需要开发者对Windows 消息驱动机制和C++ Builder 中的消息处理机制能有一个深入的认识。
1 Windows 消息驱动机制 
  Windows是以消息驱动的操作系统,Windows 消息提供了应用程序与应用程序以及应用程序与Windows系统之间进行通讯的手段。Windows 中有一个系统消息队列,对于每一个正在执行的Windows应用程序,系统为其建立一个“消息队列”,用来存放该程序可能创建的各种窗口的消息。应用程序中含有一段称作“消息循环”的代码,用来从消息队列中检索这些消息并把它们分发到相应的窗口函数中。
  消息循环代码是应用程序中主函数winmain ( )中类似如下的程序段:

  while(GetMessage(&msg,NULL,NULL,NULL))
  {
   ……
   //从消息队列中取得消息后,检索并生成字符消息
   TranslateMessage(&msg);
   //将消息发送给相应的窗口函数
   DispatchMessage(&msg);
  }

  由此可见,所谓“消息循环”,实际是程序循环。Windows 应用程序创建的每个窗口都在系统核心注册一个相应的窗口函数,窗口函数程序代码形式上是一个switch-case 语句,用以处理由消息循环发送到该窗口的消息。

  Switch(msg)
  {
  case … : …
      break;
  ……
  default : …
      break;
  }

  窗口函数由Windows 采用消息驱动的形式隐式地调用(由系统调用),而不是由应用程序显示调用的,窗口函数处理完消息后又将控制权返回给Windows。 
  Windows消息处理过程实质包括以下四个步骤:
  (1) 系统发生事件;
  (2) 根据事件产生消息,并放入消息队列;
  (3) 应用程序从消息队列中取得消息,并封装后,通过消息循环把消息分派给对应的处理函数;
  (4) 处理函数最终处理这个消息。
2 C++ Builder 中的消息处理
  在类 Application中封装、实现了Windows 程序框架,包括一些初始化、消息循环代码等。一般用C++ Builder 编写的Windows GUI 应用程序,缺省生成如下代码:

//Windows 应用程序主函数
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
//作初始化
Application-〉Initialize();
Application-〉CreateForm(__classid(TForm1), &&Form1);
//其中封装了消息循环
Application-〉Run();
}
//例外处理
catch (Exception &&exception)  
{
Application-〉ShowException(&&exception);
}
return ;
}

  对于消息处理,C++ Builder采用基于组件(component)的程序设计模式,每种控件都继承一套完整的消息派送体系。 它为每一种类型的控件都注册一个名为 MainWndProc的方法函数作为窗口函数,接受“消息循环”派送来的消息,它是一个非虚拟方法,不对任何特定消息作特别处理,它仅仅调用WndProc方法函数,并作一些例外处理。不同控件对消息处理的定制发生在WndProc 方法中,因为它是一个虚拟方法,每一种控件可以通过覆盖它来适应特别的情况。WndProc 方法检查不同的条件,作不同的处理,从而能够滤掉不希望处理的各种消息。最终,WndProc 调用 Dispatch 方法,它是一个从所有控件的始祖TObject 继承而来的虚拟方法,它确定调用哪个方法处理传来的消息。

  以上是消息在控件中的传递过程,C++ Builder对消息处理作了进一步的封装,把常用的消息封装成相应的事件属性,开发者只要把精力放在响应函数设计和建立消息与处理函数的映射关系上。
3 对VCL事件的处理
  在C++ Builder中,VCL事件包含了许多Windows消息,所以在通常情况下对Windows消息的响应就转化为对VCL事件的响应处理。VCL事件与响应函数之间的映射关系可以是静态的(程序设计时确定),也可以是动态的(在程序运行时确定)。下面分别以建立动态和静态的响应函数映射关系。
  在静态映射关系条件下,建立VCL事件的响应处理非常简单。我们以一个实例来说明。在一个新建工程中,放入一个按钮控件(TButton),在对象观察器中选择事件标签,在该标签中选择OnClick事件,写入响应函数,通常双击该事件选项,则可以很方便地建立事件与函数的对应关系和响应函数框架。

void __fastcall TMsgExp::Button1Click(TObject *Sender)
{
  //对单击按钮事件的响应
  ShowMessage("你触发了一个单击事件!");
}

  程序设计完成后,消息映射就确定下来,不会改变。
  在动态映射关系条件下,需要在运行时设定事件与函数的对应关系。C++ Builder的应用程序中的任何窗体收到一个Windows就会触发一个OnMessage事件,通过该事件来捕获发送给程序的消息,并动态地建立消息与响应函数之间的映射关系,也以一个实例来说明。
  按上面提到的例子,建立一个按钮单击事件的响应,如下:

void __fastcall TMsgExp::Button2Click(TObject *Sender)
{
//根据单选按钮的不同状态,动态建立消息与响应函数的映射
if (RadioButton1->Checked)
Application->OnMessage=ClickMouse;
if (RadioButton2->Checked)
Application->OnMessage=Move;
}
……
void __fastcall TMsgExp::ClickMouse(tagMSG & Msg, bool & Handled)
{
//对单击鼠标事件的处理
if(Msg.message==WM_LBUTTONDOWN)
{
num++;
Label1->Caption="你一共点击了"+AnsiString(num)+"次";
//允许后继过程继续处理该事件
Handled=false;
}
}
……
void __fastcall TMsgExp::Move(tagMSG & Msg, bool & Handled)
{
//对鼠标移动事件的处理
if(Msg.message==WM_MOUSEMOVE)
{
//从消息中过去鼠标的位置参数
WORD xPos = LOWORD(Msg.lParam);
WORD yPos = HIWORD(Msg.lParam);
Label1->Caption="当前鼠标位置 x:"+AnsiString(xPos)+" y:"+AnsiString(yPos);
//允许后继过程继续处理该事件
Handled=false;
}
}

  上面的例子利用Application 的OnMessage事件实现了对鼠标移动和单击事件的动态响应。需要说明的是OnMessage事件仅仅接受发送到消息队列中的消息,对于利用API直接发送给窗口函数的消息将不与理会。

4 对自定义消息的处理
  在有的情况下,程序需要发送自己定义的消息,它既可以用在两个应用程序之间的通讯,也可以用在一个程序内部的不同窗体和组件之间的通信。使用自定义消息有两个优点:一是发消息时,无须知道接受者的具体类型,只要知道窗口的句柄;二是消息可以广播给多个接受者。它也有静态和动态的两种函数响应映射关系。
  同样举一个例子,在窗体类的头文件(.h)中加入自定义消息MY_SELFDEFINE,并静态的对这个自定义消息建立函数映射关系

#define MY_SELFDEFINE (WM_USER+100)//定义响应函数
void __fastcall Handle(TMessage & Msg);
//建立响应函数(Handle)与自定义消息(MY_SELFDEFINE)的映射
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(MY_SELFDEFINE,TMessage,Handle)
//VCL_MESSAGE_HANDLER(MY_SELFDEFINE,TMessage,Handle)
END_MESSAGE_MAP(TForm)

  在窗体类的实现文件中(.cpp)分别实现定义的响应函数(Handle)和发送自定义的消息(MY_SELFDEFINE)

//响应函数的实现
void __fastcall TMsgExp::Handle(TMessage & Msg)
{
ShowMessage("拦截到自定义消息MY_SELFDEFINE");
}

  //发送自定义的消息(MY_SELFDEFINE)

  this->Perform(MY_SELFDEFINE,0,0);

  应用程序接受到自定义的消息后,根据静态建立的映射关系,将消息交由响应函数处理。

  接着上面的例子,再建立一个动态对自定义消息进行响应的例子。分别建立两个响应函数: OpenForm(打开新窗口)和CloseForm(关闭新窗口)。

void __fastcall TMsgExp::OpenForm(TMessage & Msg)
{
  //截获到MY_SELFDEFINE消息,则打开新窗口,否则交由原来的函数处理
  if (Msg.Msg == MY_SELFDEFINE)
  NewWindows->Show();
  else
  WndProc(Msg);
} void __fastcall TMsgExp::CloseForm(TMessage & Msg)
{
//截获到MY_SELFDEFINE消息,则关闭新窗口,否则交由原来的函数处理
if (Msg.Msg == MY_SELFDEFINE)
NewWindows->Hide();
else
WndProc(Msg);
}
  
//根据单选按钮的不同状态,动态建立自定义消息与响应函数的映射
if (RadioButton3->Checked)
  /*利用TControl控件的WindowProc属性,动态地设置 对处理消息的响应函数*/
  this->WindowProc=OpenForm;
if (RadioButton4->Checked)
  this->WindowProc=CloseForm;
  //发送自定义的消息(MY_SELFDEFINE)
  this->Perform(MY_SELFDEFINE,,);

5 结束语
  本文分别探讨了对Windows消息和自定义消息建立静态和动态响应函数映射的技巧和方法,阐述了由事件到消息,再由消息映射到响应函数处理的整个机制,具有很大的实用价值和理论探讨价值。示例程序在Windows2000/C++ Builder6.0环境下编译通过

BCB:Windows消息处理的更多相关文章

  1. 深入剖析MFC中对于Windows消息处理、运行机制

    序: 本人对Windows系统.MFC谈不上有深入的了解,但对MFC本身包装API的机制很有兴趣,特别是读了候老师的<深入浅出MFC>后,感觉到VISUAL C++的Application ...

  2. windows消息处理(强烈推荐,收藏)

    由于看了一下,比较好理解,暂时先放到这里,待有空再翻译.只是在每节后大致介绍一下讲的内容. 感觉写的比较全,无论从消息的原理还是从MFC操作上来说,值得一看,我也在此做个收藏. (一) 说明:以下首先 ...

  3. C#之Windows消息处理

    public enum WindowsMessage:int { /// <summary> /// /// </summary> WM_NULL = 0x0000, /// ...

  4. 《逐梦旅程 WINDOWS游戏编程之从零开始》笔记3——输入消息处理,物理建模与粒子系统初步

    第7章 Windows游戏输入消息处理 1. 键盘消息处理 之前提到的窗口过程函数有两参数与消息输出有关——wParam和llParam LRESULT CALLBACK WindowProc( _I ...

  5. Windows消息机制

    Windows的消息系统是由3个部分组成的: · 消息队列.Windows能够为所有的应用程序维护一个消息队列.应用程序必须从消息队列中获取消息,然后分派给某个窗口.· 消息循环.通过这个循环机制应用 ...

  6. Windows 窗体—— 键盘输入工作原理

    方法 注释 PreFilterMessage 此方法在应用程序级截获排队的(也称为已发送的)Windows 消息. PreProcessMessage 此方法在 Windows 消息处理前在窗体和控件 ...

  7. Windows游戏编程之从零开始d

    Windows游戏编程之从零开始d I'm back~~恩,几个月不见,大家还好吗? 这段时间真的好多童鞋在博客里留言说或者发邮件说浅墨你回来继续更新博客吧. woxiangnifrr童鞋说每天都在来 ...

  8. Windows消息拦截技术的应用

    Windows消息拦截技术的应用 民航合肥空管中心 周毅 一.前 言 众所周知,Windows程式的运行是依靠发生的事件来驱动.换句话说,程式不断等待一个消息的发生,然后对这个消息的类型进行判断,再做 ...

  9. Windows消息拦截技术的应用(作者博客里有许多相关文章)

    民航合肥空管中心 周毅 一.前 言 众所周知,Windows程式的运行是依靠发生的事件来驱动.换句话说,程式不断等待一个消息的发生,然后对这个消息的类型进行判断,再做适当的处理.处理完此次消息后又回到 ...

随机推荐

  1. 异步和多线程,委托异步调用,Thread,ThreadPool,Task,Parallel,CancellationTokenSource

    1 进程-线程-多线程,同步和异步2 异步使用和回调3 异步参数4 异步等待5 异步返回值 5 多线程的特点:不卡主线程.速度快.无序性7 thread:线程等待,回调,前台线程/后台线程, 8 th ...

  2. C++类型起别名的方式

    C++给类型起别名的方式: #include <iostream> using namespace std; #define DString std::string //! 不建议使用!t ...

  3. 洛谷 - P2324 - 骑士精神 - A*搜索

    为什么估价是16,因为最后一步复原空格可以恢复两个位置,当然设成17.18都可以. #include<bits/stdc++.h> using namespace std; typedef ...

  4. Animation Starter Pack中动画蓝图事件添加的位置

    可以直接在动画状态机的详情页添加简单事件,跟在动画里添加的通知事件效果一致

  5. AnimationCurve

    http://blog.sina.com.cn/s/blog_471132920101f8nv.html 说明:关于animationCurve的使用总结1 创建,可以新建脚本,创建animation ...

  6. uoj#308. 【UNR #2】UOJ拯救计划(并查集)

    传送门 如果把答案写出来,就是\(\sum_{i=1}^ki!\times {k\choose i}\times f_i\),其中\(f_i\)为选\(i\)种颜色方案 发现如果\(i\geq 3\) ...

  7. CC05:基本字符串压缩

    题目 利用字符重复出现的次数,编写一个方法,实现基本的字符串压缩功能.比如,字符串"aabcccccaaa"经压缩会变成"a2b1c5a3".若压缩后的字符串没 ...

  8. AI入门丨开源学习资源推荐

    现在AI大热,网上的资源也非常多,让人眼花缭乱.非科班的我,经过半年的摸索,也算马马虎虎入了坑.下面整理了我认为不错的学习资源,大部分我都看过,以分享给更多的人.我会不断保持更新,也欢迎大家补充. P ...

  9. JAVA 员工管理系统(用抽象类实现),简易版。

    package Demo513; /* 定义一个Employee类,该类包含: private 成员变量name,number,birthday,其中birthday为MyDate类的对象: abst ...

  10. AJPFX关于JDK,JRE,JVM的区别与联系

    很多朋友可能跟我一样,对JDK,JRE,JVM这三者的联系与区别,一直都是模模糊糊的. 今天我们来整理下三者的关系. JDK : Java Development ToolKit(Java开发工具包) ...