Windows的消息机制与消息重定向技术

windows消息处理机制

消息,就是指Windows发出的一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息。例如,
  对于单击鼠标所产生的消息来说,这个记录中包含了单击鼠标时的坐标。这个记录类型叫做TMsg,它
  在Windows单元中是这样声明的:
  type
  TMsg = packed record
  hwnd: HWND; / /窗口句柄
  message: UINT; / /消息常量标识符
  wParam: WPARAM ; // 32位消息的特定附加信息
  lParam: LPARAM ; // 32位消息的特定附加信息
  time: DWORD; / /消息创建时的时间
  pt: TPoint; / /消息创建时的鼠标位置
  e n d ;
  消息中有什么?
  是否觉得一个消息记录中的信息像希腊语一样?如果是这样,那么看一看下面的解释:
  hwnd 32位的窗口句柄。窗口可以是任何类型的屏幕对象,因为Win32能够维护大多数可
  视对象的句柄(窗口、对话框、按钮、编辑框等)。
  message 用于区别其他消息的常量值,这些常量可以是Windows单元中预定义的常量,也可以是自定义的常量。
  wParam 通常是一个与消息有关的常量值,也可能是窗口或控件的句柄。
  lParam 通常是一个指向内存中数据的指针。由于WParam、lParam和Pointer都是32位的,因此,它们之间可以相互转换。
  Windows的消息系统是由3个部分组成的:
  • 消息队列。Windows能够为所有的应用程序维护一个消息队列。应用程序必须从消息队列中获取
  消息,然后分派给某个窗口。
  • 消息循环。通过这个循环机制应用程序从消息队列中检索消息,再把它分派给适当的窗口,然
  后继续从消息队列中检索下一条消息,再分派给适当的窗口,依次进行。
  • 窗口过程。每个窗口都有一个窗口过程来接收传递给窗口的消息,它的任务就是获取消息然后
  响应它。窗口过程是一个回调函数;处理了一个消息后,它通常要返回一个值给Wi ndows。
  注意回调函数是程序中的一种函数,它是由Windows或外部模块调用的。
  一个消息从产生到被一个窗口响应,其中有5个步骤:
  1) 系统中发生了某个事件。
  2) Windows把这个事件翻译为消息,然后把它放到消息队列中。
  3) 应用程序从消息队列中接收到这个消息,把它存放在TMsg记录中。
  4) 应用程序把消息传递给一个适当的窗口的窗口过程。
  5) 窗口过程响应这个消息并进行处理。
  步骤3和4构成了应用程序的消息循环。消息循环往往是Windows应用程序的核心,因为消息循环
  使一个应用程序能够响应外部的事件。消息循环的任务就是从消息队列中检索消息,然后把消息传递给适当的窗口。如果消息队列中没有消息,Windows就允许其他应用程序处理它们的消息。
  Windows操作系统最大的特点就是其图形化的操作界面,其图形化界面是建立在其消息处理机制这个基础之上的。如果不理解Windows消息处理机制,肯定无法深入的理解Windows编程。可惜很多程序员对Windows消息只是略有所闻,对其使用知之甚少,更不了解其内部实现原理,本文试着一步一步向大家披露我理解的Windows消息机制。可以说,掌握了这一部分知识,就是掌握了Windows编程中的神兵利器,灵活运用它,将会极大的提高我们的编程能力。
  一、 消息概述Windows窗体是怎样展现在屏幕上的呢?众所周知,是通过API绘制实现的。Windows操作系统提供了一系列的API函数来实现界面的绘制功能,例如:
  2 DrawText绘制文字
  2 DrawEdge绘制边框
  2 DrawIcon绘制图标
  2 BitBlt 绘制位图
  2 Rectangle绘制矩形
  2 …
  再复杂的程序界面都是通过这个函数来实现的。
  那什么时候调用这些函数呢?显然我们需要一个控制中心,用来进行“发号施令”,我们还需要一个命令传达机制,将命令即时的传达到目的地。这个控制中心,就是一个动力源,就像一颗心脏,源源不断地将血液送往各处。这个命令传达机制就是Windows消息机制,Windows消息就好比是身体中的血液,它是命令传达的使者。
  Windows消息控制中心一般是三层结构,其顶端就是Windows内核。Windows内核维护着一个消息队列,第二级控制中心从这个消息队列中获取属于自己管辖的消息,后做出处理,有些消息直接处理掉,有些还要发送给下一级窗体(Window)或控件(Control)。第二级控制中心一般是各Windows应用程序的Application对象。第三级控制中心就是Windows窗体对象,每一个窗体都有一个默认的窗体过程,这个过程负责处理各种接收到的消息。如下图所示:

windows消息处理机制
说明图
  (注:windows指windows操作系统;窗口:即windows窗口;窗体:包括窗口,以及有句柄的控件;control指控件,控件本身也可能是一个window,也可能不是;Application即应用程序,应用程序也可能不会用到Windows消息机制,这里我们专门讨论有消息循环的应用程序)
  消息是以固定的结构传送给应用程序的,结构如下:
  Public Type MSG
  hwnd As Long
  message As Long
  wParam As Long
  lParam As Long
  time As Long
  pt As POINTAPI
  End Type
  其中hwnd是窗体的句柄,message是一个消息常量,用来表示消息的类型,wParam和lParam都是32位的附加信息,具体表示什么内容,要视消息的类型而定,time是消息发送的时间,pt是消息发送时鼠标所在的位置。
  Windows操作系统中包括以下几种消息:
  1、标准Windows消息:
  这种消息以WM_打头。
  2、通知消息
  通知消息是针对标准Windows控件的消息。这些控个包括:按钮(Button)、组合框(ComboBox)、编辑框(TextBox)、列表框(ListBox)、ListView控件、Treeview控件、工具条(Toolbar)、菜单(Menu)等。每种消息以不同的字符串打头。
  3、自定义消息
  编程人员还可以自定义消息。
  二、 关于Windows句柄
  不是每个控件都能接收消息,转发消息和绘制自身,只有具有句柄(handle)的控件才能做到。有句柄的控件本质上都是一个窗体(window),它们可以独立存在,可以作为其它控件的容器,而没有句柄的控件,如Label,是不能独立存在的,只能作为窗口控件的子控件,它不能绘制自身,只能依靠父窗体将它绘制来。
  句柄的本质是一个系统自动维护的32位的数值,在整个操作系统的任一时刻,这个数值是唯一的。但该句柄代表的窗体释放后,句柄也会被释放,这个数值又可能被其它窗体使用。也就是说,句柄的数值是动态的,它本身只是一个唯一性标识,操作系统通过句柄来识别和查找它所代表的对象。
  然而,并非所有的句柄都是窗体的句柄,Windows系统中还中很多其它类型的句柄,如画布(hdc)句柄,画笔句柄,画刷句柄,应用程序句柄(hInstance)等。这种句柄是不能接收消息的。但不管是哪种句柄,都是系统中对象的唯一标识。本文只讨论窗体句柄。
  那为什么句柄使窗口具有了如此独特的特性呢?实际是都是由于消息的原因。由于有了句柄,窗体能够接收消息,也就知道了该什么时候绘制自己,绘制子控件,知道了鼠标在什么时候点击了窗口的哪个部分,从而作出相应的处理。句柄就好像是一个人的身份证,有了它,你就可以从事各种社会活动;否则的话,你要么是一个社会看不到的黑户,要么跟在别人后面,通过别人来证明你的存在。
  三、 消息的传送
  1、从消息队列获取消息:
  可以通过PeekMessage或GetMessage函数从Windows消息队列中获取消息。Windows保存的消息队列是以线程(Thread)来分组的,也就是说每个线程都有自己的消息队列。
  2、发送消息
  发送消息到指定窗体一般通过以下两个函数完成:SendMessage和PostMessage。两个函数的区别在于:PostMessage函数只是向线程消息队列中添加消息,如果添加成功,则返回True,否则返回False,消息是否被处理,或处理的结果,就不知道了。而SendMessage则有些不同,它并不是把消息加入到队列里,而是直接翻译消息和调用消息处理(线程向自己发送消息才是这样),直到消息处理完成后才返回。所以,如果我们希望发送的消息立即被执行,就应该调用SendMessage。
  还有一点,就是SendMessage发送的消息由于不会被加入到消息队列中(错:线程向其他线程发送消息也是追加到其他线程的发送消息队列的,即使这两个线程在同一个进程也是如此),所以通过PeekMessage或GetMessage是不能获取到由SendMessage发送的消息。
  另外,有些消息用PostMessage不会成功,比如wm_settext。所以不是所有的消息都能够用PostMessage的。
  还有一些其它的发送消息API函数,如PostThreadMessage,SendMessageCallback,SendMessageTimeout,SendNotifyMessage等。
  四、 消息循环与窗体过程
  消息循环是应用程序能够持续存在的根本原因。如果循环退出,则应用程序就结束了。
  我们来看一看Delphi中封装的消息循环是怎样的:
  第一步:程序开始运行(Run)
  Application.Initialize;//初始化
  Application.CreateForm(TForm1, Form1);//创建主窗体
  Application.Run;//开始运行,准备进行消息循环
  如果不创建主窗体,应用程序同样可以存在和运行。
  第二步:开始调用消息循环(HandleMessage)
  procedure TApplication.Run;
  begin
  FRunning := True;
  try
  AddExitProc(DoneApplication);
  if FMainForm <> nil then
  begin
  case CmdShow of
  SW_SHOWMINNOACTIVE: FMainForm.FWindowState := wsMinimized;
  SW_SHOWMAXIMIZED: MainForm.WindowState := wsMaximized;
  end;
  if FShowMainForm then
  if FMainForm.FWindowState = wsMinimized then
  Minimize else
  FMainForm.Visible := True;
  Repeat//注:循环开始
  try
  HandleMessage;
  except
  HandleException(Self);
  end;
  until Terminated;//循环结束条件
  end;
  finally
  FRunning := False;
  end;
  end;
  第三步:消息循环中对消息的处理。
  procedure TApplication.HandleMessage;
  var
  Msg: TMsg;
  begin
  if not ProcessMessage(Msg) then Idle(Msg);
  end;
  function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
  var
  Handled: Boolean;
  begin
  Result := False;
  if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
  begin
  Result := True;
  if Msg.Message <> WM_QUIT then
  begin
  Handled := False;
  if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
  if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and
  not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
  begin
  TranslateMessage(Msg);
  DispatchMessage(Msg);
  end;
  end
  else
  FTerminate := True;
  end;
  end;
  窗体过程实际上是一个回调函数。所谓的回调函数,实际上就是由Windows操作系统或外部程序调用的函数。回调函数一般都有规定的参数格式,以地址方式传递给调用者。窗口过程中是Windows操作系统调用了,在一个窗口创建的时候,在分配窗体句柄的时候就需要传入回调函数地址。那为什么我们平时编程看不到这个回调函数呢?这是由于我们的编程工具已经为我们生成了默认的窗体过程,这个过程的要做的事情就是判断不同的消息类型,然后做出不同的处理。例如可以为键盘或鼠标输入生成事件等。
  五、 消息与事件
  事件本质上是对消息的封装,是IDE编程环境为了简化编程而提供的有用的工具。这个封装是在窗体过程中实现的。每种IDE封装了许多Windows的消息,例如:
  事件
  消息
  OnActivate
  WM_ACTIVATE
  OnClick
  WM_XBUTTONDOWN
  OnCreate
  WM_CREATE
  OnDblClick
  WM_XBUTTONDBLCLICK
  OnKeyDown
  WM_KEYDOWN
  OnKeyPress
  WM_CHAR
  OnKeyUp
  WIN_KEYUP
  OnPaint
  WM_PAINT
  OnResize
  WM_SIZE
  OnTimer
  WM_TIMER


消息重定向技术

消息机制和绘图机制是微软Windows及其周边其它产品与生俱来的,是Win 系列OS作为一个操作系统进行微机内部实现的二大支柱和特征,消息系统是Windows下一切应用程序间,包括Windows自身,进行交互和通讯的渠道,是Windows实现对运行在其下的所有应用程序进行控制及应用程序对Windows进行响应的解决手段,因此对Windows的编程,无论是在哪种 语言规范和IDE 下,都不可避免地要涉及到消息处理,虽然有些编程语言如 VB 用事件驱动编程机制在很大程度上封装了消息的复杂性,但若要深入Win32编程,就必须学习Windows的消息系统,正如游戏编程要掌握Win的绘图机制一样,而只要你一旦深韵了这二大支柱和基本,你就掌握了Win32编程的根本。。

   消息的产生来源于系统事情(包括计时器事件)和用户事情,Windows用消息来调入和关闭(还有其它处理,如绘制一个窗口等)应用程序,一个典型表现是在关机操作中,Windows发一个关机的消息给所有正在运行的应用程序,告知它们退出内存,此时,应用程序用回应消息的方法来响应OS,因此,消息是应用程序与WinOS交互的手段..
   消息的主体是应用程序之间和应用程序与 OS 之间,(这是通俗的说法,其实在一个应用程序的内部,各“窗口”组件之间也存在着消息的流动,窗口组件与它们的父窗口和上层窗口之间当然也有消息的传递过程(如"命令传递",后面在跟踪一个消息的路径中将会详谈),Windows内部即时流动的消息数量是如此的宠大,程序实现之外的手工分析是一种很自不量力的事情)消息的最终主体却是窗口与窗口之间,窗口与OS之间 - 因为在MFC的技术规范里,只有窗口进程才能发送和接收一个消息并处理它,当然一些非界面窗口类如文档类也能处理一个消息,消息的最终归宿是某个窗口类的成员函数,也就是进入消息处理函数被处理,或被某个非界面类也就是内部处理类如文档类处理,系统中默认的窗口类和用户注册的窗口类都有进程,都能在内存中创建实在的窗口对象,窗口对象和窗口类接收和处理(千万注意:接收一个消息和处理一个消息是相差甚大的二个过程,后面将在讨论重定向一个消息技术时将谈到)发往它或由它主动发往别的窗口进程或OS的消息,修改窗口进程干涉窗口进程对消息的处理过程(而不是接收过程,这个区别的详细解释请参见后面从"注意消息泵并不是一个.."起的文字)是可能的(窗口进程只是一段函数),但是如果这个窗口进程属于别人,如系统的窗口类,你将没有源程序进行修改,但却可以用消息重定的技术加以干涉,比如用户自定义的窗口类,用户完全可以自定义它的窗口进程,编写自己的消息泵,实现对消息的重定向,编写用户自己的消息泵属于Win32编程中重定向一个消息的七大技术之一。
   MFC中有七种技术可以用来重定向一个消息,它们是:1,子分类2,超分类3,OnCmdMsg(),4,SetCapture5,编写自己的消息泵,6,SetWindowsHookEx(),人们常说的钩子函数,便是其中之一.
   在谈完消息泵的概念后,我们将一步一步追踪一个消息在系统中的路径,然后才能讨论对它的重定向。

   消息泵并不是一个窗口类的窗口进程,虽然它们都是函数,同样都对注入到这个窗口进程的消息进行工作,而并不最终处理消息本身(上面已经说到原因),消息泵是一个通俗的说法,它只与消息被发往窗口进程后的接收工作有关而不与处理过程有关(上面也已经说到消息的接收和处理是二不同过程),而窗口进程恰恰相反它只与处理有关不与接收有关下面开始详述。。

消息泵被包含在 CWinApp 的成员函数Run()中..(

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本软件包含windows和linux两种版本 1、 了解NC的用法 命令:   nc –h 技巧:   win98用户可以在autoexec.bat加入path=nc的路径,win2000用户在环境变量中加入path中,linux含有这个命令(redhat) 1、基本使用   想要连接到某处: nc [-options] hostname port[s] [ports] ...   绑定端口等待连接: nc -l -p port [-options] [hostname] [port]   参数:   -e prog 程序重定向,一旦连接,就执行 [危险!!]   -g gateway source-routing hop point[s], up to 8   -G num source-routing pointer: 4, 8, 12, ...   -h 帮助信息   -i secs 延时的间隔   -l 监听模式,用于入站连接   -n 指定数字的IP地址,不能用hostname   -o file 记录16进制的传输   -p port 本地端口号   -r 任意指定本地及远程端口   -s addr 本地源地址   -u UDP模式   -v 详细输出——用两个-v可得到更详细的内容   -w secs timeout的时间   -z 将输入输出关掉——用于扫描时   其中端口号可以指定一个或者用lo-hi式的指定范围。   例如:扫描端口   tcp扫描   C:\nc>nc -v -z -w2 192.168.0.80 1-140   net [192.168.0.80] 140 (?)   net [192.168.0.80] 139 (netbios-ssn) open   net [192.168.0.80] 138 (?)   net [192.168.0.80] 137 (netbios-ns)   net [192.168.0.80] 136 (?)   net [192.168.0.80] 135 (epmap) open   net [192.168.0.80] 81 (?) open   net [192.168.0.80] 80 (http) open   net [192.168.0.80] 79 (finger)   net [192.168.0.80] 25 (smtp) open   net [192.168.0.80] 24 (?)   net [192.168.0.80] 23 (telnet)   net [192.168.0.80] 21 (ftp)   udp扫描   C:\nc>nc -u -v -z -w2 192.168.0.80 1-140   net [192.168.0.80] 140 (?) open   net [192.168.0.80] 139 (?) open   net [192.168.0.80] 138 (netbios-dgm) open   net [192.168.0.80] 137 (netbios-ns) open   net [192.168.0.80] 54 (?) open   net [192.168.0.80] 53 (domain) open   net [192.168.0.80] 38 (?) open   net [192.168.0.80] 37 (time) open   net [192.168.0.80] 7 (echo) open 2、高级应用   1.Window用法:   (1)IE的MIME欺骗   www.try2hack..nl(是一个让初学黑客技术的人去做实验的站点)   打开这个页面,有http://www.try2hack/cgi-bin/level7页面(这个网站提供了黑客的8关,过了这8关证明你开始入门了),这个页面告诉我们的浏览器不是 IE6.72,我们的操作系统不是LIUNX,我们不是从www.microsoft/ms重定向链接过去的,有病阿(这是一个题目呀,要慢慢研究),linux有IE6.72? 微软会在它的页面上放的链接?看看页面的源代码,是在服务器端 执行的perl脚本,根本无法看到,还是研究一下IE5和它通信时都告诉了它什么,抓包,我们会发现,我们的IE5告诉对方: 我是MSIE 5.0; Windows NT 5.0; .NET CLR 1.0.3705。。。。哈哈,这个cgi应该是根据这些信息知道我们不是它要求的 客户,嘿嘿,需要欺骗对方才行,用军刀来可以实现,如下做就可

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值