[转]Windows 消息机制

帮助理解消息 

转自http://tb.blog.csdn.net/TrackBack.aspx?PostId=11119

使用过Delphi的朋友都知道,Delphi是一个真正面向对象的编程环境,但是不但如此,Delphi的这种面向对象的机制是单纯的建立在Windows的消息机制上的Delphi代码,而不是像VB、VFP之类的调用DLL、OCX,通过查看Delphi控件的源代码,你可以知道整个机制是怎样组织起来的,而且你可以完完全全地控制这些控件,因为它们只是用Delphi代码编写的,而不是存在在DLL中看不见的东西。
  那么Windows所谓的消息(Message)机制到底是什么呢?还记得以前学Basic的时候,根本没有什么事件之类的东西,整个程序是用流程图来描绘的,在程序需要键盘输入的地方,整个程序就停下来等待输入,然后根据输入来做不同的事情。这样做本来是没什么问题的,但是到了图形界面的时候,情况不同了,鼠标输入成了一个很大的问题,而且在Windows这样的多任务系统下,不可能让一个程序不断的测试设备状态那样子来获得输入。
  总之呢,Windows下的消息机制是完全不同的,即使与单纯的中断事件比起来,还是有很多不同的地方。
  简单的说来,一个线程在创建窗体的时候,会自动生成一个消息队列,但窗体不是必要的,可以通过其他途径来创建消息队列。然后,其他的程序,或者Windows系统本身,可以向这个线程发送消息到它的消息队列中,通知这个线程有什么东西发生了,这就是所谓的事件。每个进程都可以使用GetMessage函数获得它的消息队列中最前面的一个消息,GetMessage同时会自动将此消息从消息队列中删除掉,当然也可以指定不删除消息,这个以后再说。光听可能还是不能想象出来,那就看以下的例子吧:

program Sample3;

uses
 Windows,
 Messages;

var Msg: TMsg;

begin
  PostMessage(0, WM_USER, 0, 0); // 首先强制生成消息队列
  PeekMessage(Msg, 0, WM_USER, WM_USER, 0);
  // 然后这里就是所谓的消息循环,只有当收到WM_QUIT的消息时,GetMessage()才会返回False

  while GetMessage(Msg, 0, 0, 0) do begin
  end;
  // 这里可以做程序结束前(收到WM_QUIT后)的工作

end.

  这个程序运行后不会干任何事情,同时也会忽略一切Windows发给它的消息,除了WM_QUIT之外,因为GetMessage这个函数有一个特点,当收到其他消息的时候,GetMessage的返回值是TRUE,而在收到WM_QUIT的时候返回值则为FALSE,因此消息循环就被打破了。在Windows关闭的时候,Windows会自动发一个WM_QUIT的消息到这个程序的主线程,然后程序就退出了。
  绝大多数的程序的主体就是这个样子,都有一个消息循环,也就是说,每一个程序都是不断的使用GetMessage尝试获得新的消息,然后处理,周而复始,直到收到WM_QUIT为止。而其中高明之处,就是GetMessage在被调用的时候,如果检查出消息队列中没有消息,则函数不会马上返回,而是使线程转入睡眠状态,因而线程不会因为不断的循环而浪费CPU时间。在有新的消息收到之后,线程会重新苏醒,GetMessage把收到的消息放到一个TMsg类型的参数里面返回,于是程序就可以处理这个消息了。
  好了,这个消息机制是如何和窗体程序结合在一起的呢?换句话说,如果程序生成了窗体,那么程序又如何通过这个消息机制获取用户的输入消息呢?这就要从创建窗体的过程说起了。以下是一个比较复杂一点的例子:

program Sample4;

uses
 Windows,
 Messages;

var
 Msg: TMsg;
 wc: TWndClass; // RegisterClass()所需要的参数
 hWnd: THandle; // 主窗体的句柄

const
 ClassName = 'MainWClass';

function MainWndProc(Handle: THandle; MsgID: UINT; wParam, lParam: Integer): LRESULT; stdcall;
begin
  Result := 1;
  case MsgID of

    WM_CLOSE: begin // 关闭窗体所产生的消息
      if MessageBox(Handle,
        '要关闭这个程序吗?',
        '例子程序-4',
        MB_ICONQUESTION or MB_YESNO
          ) = IDYES then
        DestroyWindow(hWnd)
      else
        Result := 0;
      Exit;
    end;

    WM_DESTROY: begin // DestroyWindow()所产生的消息
      PostQuitMessage(0);
    end;

  end;

  // 剩下的消息交给Windows预设的处理函数就可以了,比如画窗体的WM_NCPAINT消息等
  Result := DefWindowProc(Handle, MsgID, wParam, lParam);
end;

begin
  // 首先使用RegisterClass()注册窗体的类,这可不是Delphi数据类型中的类哦!
  wc.style := CS_HREDRAW or CS_VREDRAW;
  wc.lpfnWndProc := @MainWndProc; // 消息处理函数的地址
  wc.hInstance := hInstance; // 程序的句柄,同时也是基地址
  wc.hIcon := LoadIcon(0, PChar(IDI_APPLICATION));
  wc.hCursor := LoadCursor(0, IDC_ARROW); // 图标
  wc.hbrBackground := GetStockObject(WHITE_BRUSH); // 背景画刷
  wc.lpszClassName := ClassName; // 前面定义的常量
  if RegisterClass(wc) = 0 then Halt(0);
  hWnd := CreateWindowEx(0,
    ClassName, // 刚才注册的类的名称
    'Sample', // 窗体的标题
    WS_OVERLAPPEDWINDOW, // 窗体有标题栏、系统菜单、最大小化菜单,以及拉伸边框
    Integer(CW_USEDEFAULT),
    Integer(CW_USEDEFAULT),
    Integer(CW_USEDEFAULT),
    Integer(CW_USEDEFAULT),
    0,
    0,
    hInstance,
    nil
  );
  if hWnd = 0 then Halt(0);
  ShowWindow(hWnd, CmdShow);
  UpdateWindow(hWnd);
  while GetMessage(Msg, 0, 0, 0) do begin
    TranslateMessage(Msg);
    DispatchMessage(Msg); // 该API将消息分派到相应的窗体消息处理函数
  end;
  ExitCode := Msg.wParam;
end.

  由于要说的东西比较多,其中的API说明、定义就请各位自己查看SDK了。在创建窗体之前,首先需要向Windows注册窗体的类。所谓的注册窗体类,就是要填充一个TWndClass结构的数据,设定这个类的属性,然后传递给RegisterWindowClass()。在这些属性当中,就包括这个类的窗体消息处理函数的指针,然后还有这个类的名称。在用CreateWindowEx创建主窗体的时候,就可以根据类的名称创建这个类的窗体了。那么窗体消息处理函数是用来干什么的呢?
  需要注意的是,一个线程可以创建多个窗体,这些窗体可以是你的程序创建的,也可能是Windows在你的程序运行过程当中创建的,比如用户点击窗体左上角的系统图标时Windows会生成一个系统菜单——在Windows里,所有你能看到的东西,包括编辑框、按钮这些东西都叫做窗体,并非程序主窗体、子窗体才叫做窗体的哦!那么问题也就随之而来了,一个线程中消息队列只有一个,但窗体有这么多,如果所有消息都在主程序的消息循环中处理,那么编写大的程序将非常困难,而且结构混乱的程序维护起来也很麻烦。于是这个窗体消息处理函数(Window Procedure)就起作用了,由于可以为每一个类编写消息处理函数,因此只需要在收到相应窗体的消息的时候,把消息传递给相应类的消息处理函数处理就可以了,整个程序就变得十分结构化。程序中甚至不需要记录每一个窗体对应的类消息处理函数是哪一个,直接调用DispatchMessage() Windows就会自动使用相应的消息处理函数了。
  另一个函数DefWindowPro()也是一个关键之处,试一下把这一行省略,看看程序运行后有什么效果?事实上,这样做之后,甚至连窗体你都不会看得到,Windows只是为你所创建的窗体在屏幕上保留了一个位置。为什么呢?你的程序并没有画窗体啊!别以为创建了窗体之后Windows就会为你完成一切,其实是DefWindowProc()处理了WM_PAINT和WM_NCPAINT的消息,完成了画窗体的工作,DefWindowProc为一般的程序主窗体做了很多这样的幕后工作哦!也由此可见,消息并不只是用户鼠标、键盘的输入消息,而是程序和Windows系统的联系工具,以后就会知道,消息还有很多用处呢,而编写Delphi控件很多时候也离不开消息。好了,这一章就讲到这里吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Creating Windows CreateMDIWindow CreateWindow CreateWindowEx RegisterClass RegisterClassEx UnregisterClass Message Processing BroadcastSystemMessage CallNextHookEx CallWindowProc DefFrameProc DefMDIChildProc DefWindowProc DispatchMessage GetMessage GetMessageExtraInfo GetMessagePos GetMessageTime GetQueueStatus InSendMessage PeekMessage PostMessage PostQuitMessage PostThreadMessage RegisterWindowMessage ReplyMessage SendMessage SendMessageCallback SendMessageTimeout SendNotifyMessage SetMessageExtraInfo SetWindowsHookEx TranslateMessage UnhookWindowsHookEx WaitMessage Window Information AnyPopup ChildWindowFromPoint ChildWindowFromPointEx EnableWindow EnumChildWindows EnumPropsEx EnumThreadWindows EnumWindows FindWindow FindWindowEx GetClassInfoEx GetClassLong GetClassName GetClientRect GetDesktopWindow GetFocus GetForegroundWindow GetNextWindow GetParent GetProp GetTopWindow GetWindow GetWindowLong GetWindowRect GetWindowText GetWindowTextLength IsChild IsIconic IsWindow IsWindowEnabled IsWindowUnicode IsWindowVisible IsZoomed RemoveProp SetActiveWindow SetClassLong SetFocus SetForegroundWindow SetParent SetProp SetWindowLong SetWindowText WindowFromPoint Processes and Threads CreateEvent CreateMutex CreateProcess CreateSemaphore CreateThread DeleteCriticalSection DuplicateHandle EnterCriticalSection ExitProcess ExitThread GetCurrentProcess GetCurrentProcessId GetCurrentThread GetCurrentThreadId GetExitCodeProcess GetExitCodeThread GetPriorityClass GetThreadPriority GetWindowThreadProcessId InitializeCriticalSection InterlockedDecrement InterlockedExchange InterlockedIncrement LeaveCriticalSection OpenEvent OpenMutex OpenProcess OpenSemaphore PulseEvent ReleaseMutex ReleaseSemaphore ResetEvent ResumeThread SetEvent SetPr

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值