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. Vue.js中,如何自己维护路由跳转记录?

    在Vue的项目中,如果我们想要做返回.回退操作时,一般会调用router.go(n)这个api,但是实际操作中,使用这个api有风险,就是会让用户跳出当前应用,因为它记录的是浏览器的访问记录,而不是你 ...

  2. Codeforces Round #459 (Div. 2):D. MADMAX(记忆化搜索+博弈论)

    题意 在一个有向无环图上,两个人分别从一个点出发,两人轮流从当前点沿着某条边移动,要求经过的边权不小于上一轮对方经过的边权(ASCII码),如果一方不能移动,则判负.两人都采取最优策略,求两人分别从每 ...

  3. Codeforces Round #459 (Div. 2)The Monster[匹配问题]

    题意 给一个序列,包含(,),?,?可以被当做(或者),问你这个序列有多少合法的子序列. 分析 n^2枚举每一个子序列,暂时将每个?都当做右括号,在枚举右端点的时候同时记录两个信息:当前左括号多余多少 ...

  4. aimOffset注意事项

    AimOffset的记录 AimOffset是什么,就是动画(相对于某个具体姿势比如待机动作的)叠加. AimOffset有什么用,简单说就是叠加动作,比如无双中骑马挥刀动作叠加. 注意步骤 1所有分 ...

  5. TemplateText TT 在Runtime发生 Could not load type ...... because the format is invalid

    Severity Code Description Project File Line Suppression State Error Running transformation: System.T ...

  6. Codevs 1688 求逆序对(权值线段树)

    1688 求逆序对  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解  查看运行结果     题目描述 Description 给定一个序列a1,a2,…, ...

  7. 洛谷P3645 [APIO2015]雅加达的摩天楼(最短路+分块)

    传送门 这最短路的建图怎么和网络流一样玄学…… 一个最朴素的想法是从每一个点向它能到达的所有点连边,边权为跳的次数,然后跑最短路(然而边数是$O(n^2)$除非自创复杂度比spfa和dijkstra还 ...

  8. Luogu P4403 [BJWC2008]秦腾与教学评估【二分答案】By cellur925

    题目传送门 这道题:真·凉心出题人. 二分答案,个人感觉其实并不只适用于有明显的"最大值最小/最小值最大"条件的题目,其实也可以称它为一种"优化的暴力".这题就 ...

  9. docker镜像创建

    1. 首选要创建一个Dockerfile文件,内容如下: FROM alpine:latest MAINTAINER  lobin <lobin.hotmail.com> RUN apk ...

  10. mysql5.7安装部署后初始密码查看以及修改

    一.查看初始密码以下两种方法: 1.找到自己的error.log日志文件,执行自己的命令,红色标记的部分为初始化密码. grep 'temporary password' /data/mysql/er ...