windows消息分发机制

来源:互联网 发布:pitta mask 口罩 知乎 编辑:程序博客网 时间:2024/06/06 20:40

在TObject类中,有一个Dispatch()方法和一个DefaultHandler()方法,它们都是与消息分发机制相关的。Dispatch()负责将特定的消息分发给合适的消息处理函数。首先它会在对象本身类型的类中寻找该消息的处理函数,如果找到,则调用它;如果没有找到而该类覆盖了TObject的DefaultHandler(),则调用该类的DefaultHandler();如果两者都不存在,则继续在其基类中寻找,直至寻找到TObject这一层,而TObject已经提供了默认的DefaultHandler()方法。首先自定义一个消息结构TMyMsg,它是我们自定义的消息记录类型。对于自定义的消息类型,VCL只规定它的首4字节必须是消息编号,其后的数据类型任意。同时,VCL也提供了一个TMessage类型用于传递消息。在此程序中,不使用TMessage,而用TMyMsg代替:

[delphi] view plaincopy
  1. type  
  2. TMyMsg = record // 自定义消息结构  
  3. Msg : Cardinal; // 首4字节必须是消息编号  
  4. MsgText : ShortString; // 消息的文字描述  
  5. end;  

TMyMsg记录类型的第2个域我们定义为MsgText,由该域的字符串来给出对这个消息的具体描述信息。当然,这些信息都是由消息分发者给出的。
然后,定义一个类,由它接受外界发送给它的消息。这个类可以说明这个演示程序的核心问题。

[delphi] view plaincopy
  1. TMsgAccepter = class // 消息接收器类  
  2. private  
  3. // 编号为2000的消息处理函数  
  4. procedure AcceptMsg2000(var msg : TMyMsg); message 2000;  
  5. // 编号为2002的消息处理函数  
  6. procedure AcceptMsg2002(var msg : TMyMsg); message 2002;  
  7. public  
  8. procedure DefaultHandler(var Message); override; //默认处理方法  
  9. end;  

在Object Pascal中,指明类的某个方法为某一特定消息的处理函数,则在其后面添加message关键字与消息值,以此来通知编译器。正如上面类定义中的

[delphi] view plaincopy
  1. procedure AcceptMsg2000(var msg : TMyMsg); message 2000;  

指明AcceptMsg2000()方法用来处理值为2000的消息,该消息以及参数将通过msg参数传递给处理函数。TMsgAccepter类除提供了值为2000和2002的两个消息的处理函数外,还提供了一个默认的消息处理方法DefaultHandler()。该方法是在TObject中定义的虚方法,而在TMsgAccepter类中覆盖(override)了该方法,重新给出了新的实现。TMyMsg结构声明与TMsgAccepter类的声明与实现都被定义在MsgDispTest单元中。


TControl与Windows消息的封装

TObject提供了最基本的消息分发和处理的机制,而VCL真正对Windows系统消息的封装则是在TControl中完成的。TControl将消息转换成VCL的事件,以将系统消息融入VCL框架中。

窗口函数(WndProc)处理消息

窗口函数是一个回调函数,它被Windows系统所调用,其参数会被给出消息编号、消息参数等信息,以便进行处理。典型的窗口函数中会包含一个大的case分支,以处理不同的消息。现在就来看一下这个WndProc,请注意其中的注释:

[delphi] view plaincopy
  1. procedure TApplication.WndProc(var Message: TMessage);  
  2. type // 函数内嵌定义的类型,只限函数内部使用  
  3. TInitTestLibrary = function(Size: DWord; PAutoClassInfo: Pointer):  
  4. Boolean; stdcall;  
  5. var  
  6. I: Integer;  
  7. SaveFocus, TopWindow: HWnd;  
  8. InitTestLibrary: TInitTestLibrary;  
  9. // 内嵌函数,默认的消息处理  
  10. // 调用Windows的API函数DefWindowProc  
  11. procedure Default;  
  12. begin  
  13. with Message do  
  14. Result := DefWindowProc(FHandle, Msg, WParam, LParam);  
  15. end;  
  16. procedure DrawAppIcon;  
  17. var  
  18. DC: HDC;  
  19. PS: TPaintStruct;  
  20. begin  
  21. with Message do  

[delphi] view plaincopy
  1. begin  
  2. DC := BeginPaint(FHandle, PS);  
  3. DrawIcon(DC, 00, GetIconHandle);  
  4. EndPaint(FHandle, PS);  
  5. end;  
  6. end;  
  7. begin  
  8. try  
  9. Message.Result := 0;  
  10. for I := 0 to FWindowHooks.Count - 1 do  
  11. if TWindowHook(FWindowHooks[I]^)(Message) then Exit;  
  12. CheckIniChange(Message);  
  13. with Message do  
  14. // 开始庞大的case分支,对不同的消息做出不同的处理  
  15. case Msg of  
  16. WM_SYSCOMMAND:  
  17. case WParam and $FFF0 of  
  18. SC_MINIMIZE: Minimize;  
  19. SC_RESTORE: Restore;  
  20. else  
  21. Default;  
  22. end;  
  23. WM_CLOSE:  
  24. if MainForm <> nil then MainForm.Close;  
  25. WM_PAINT:  
  26. if IsIconic(FHandle) then DrawAppIcon else Default;  
  27. WM_ERASEBKGND:  
  28. begin  
  29. Message.Msg := WM_ICONERASEBKGND;  
  30. Default;  
  31. end;  
  32. WM_QUERYDRAGICON:  
  33. Result := GetIconHandle;  
  34. WM_SETFOCUS:  
  35. begin  
  36. PostMessage(FHandle, CM_ENTER, 00);  
  37. Default;  
  38. end;  
  39. WM_ACTIVATEAPP:  
  40. begin  

[delphi] view plaincopy
  1. Default;  
  2. FActive := TWMActivateApp(Message).Active;  
  3. if TWMActivateApp(Message).Active then  
  4. begin  
  5. RestoreTopMosts;  
  6. PostMessage(FHandle, CM_ACTIVATE, 00)  
  7. end  
  8. else  
  9. begin  
  10. NormalizeTopMosts;  
  11. PostMessage(FHandle, CM_DEACTIVATE, 00);  
  12. end;  
  13. end;  
  14. WM_ENABLE:  
  15. if TWMEnable(Message).Enabled then  
  16. begin  
  17. RestoreTopMosts;  
  18. if FWindowList <> nil then  
  19. begin  
  20. EnableTaskWindows(FWindowList);  
  21. FWindowList := nil;  
  22. end;  
  23. Default;  
  24. end else  
  25. begin  
  26. Default;  
  27. if FWindowList = nil then  
  28. FWindowList := DisableTaskWindows(Handle);  
  29. NormalizeAllTopMosts;  
  30. end;  
  31. WM_CTLCOLORMSGBOX..WM_CTLCOLORSTATIC:  
  32. Result := SendMessage(LParam, CN_BASE + Msg, WParam, LParam);  
  33. WM_ENDSESSION:  
  34. if TWMEndSession(Message).EndSession then FTerminate := True;  
  35. WM_COPYDATA:  
  36. if (PCopyDataStruct(Message.lParam)^.dwData = DWORD($DE534454))  
  37. and (FAllowTesting) then  
  38. if FTestLib = 0 then  
  39. begin  
  40. FTestLib := SafeLoadLibrary('vcltest3.dll');  
  41. if FTestLib <> 0 then  

[delphi] view plaincopy
  1. begin  
  2. Result := 0;  
  3. @InitTestLibrary := GetProcAddress(  
  4. FTestLib,  
  5. 'RegisterAutomation'  
  6. );  
  7. if @InitTestLibrary <> nil then  
  8. InitTestLibrary(  
  9. PCopyDataStruct(Message.lParam)^.cbData,  
  10. PCopyDataStruct(Message.lParam)^.lpData  
  11. );  
  12. end  
  13. else  
  14. begin  
  15. Result := GetLastError;  
  16. FTestLib := 0;  
  17. end;  
  18. end  
  19. else  
  20. Result := 0;  
  21. CM_ACTIONEXECUTE, CM_ACTIONUPDATE:  
  22. Message.Result := Ord(DispatchAction(  
  23. Message.Msg,  
  24. TBasicAction(Message.LParam))  
  25. );  
  26. CM_APPKEYDOWN:  
  27. if IsShortCut(TWMKey(Message)) then Result := 1;  
  28. CM_APPSYSCOMMAND:  
  29. if MainForm <> nil then  
  30. with MainForm do  
  31. if (Handle <> 0and IsWindowEnabled(Handle) and  
  32. IsWindowVisible(Handle) then  
  33. begin  
  34. FocusMessages := False;  
  35. SaveFocus := GetFocus;  
  36. Windows.SetFocus(Handle);  
  37. Perform(WM_SYSCOMMAND, WParam, LParam);  
  38. Windows.SetFocus(SaveFocus);  
  39. FocusMessages := True;  
  40. Result := 1;  
  41. end;  
  42. CM_ACTIVATE:  
[delphi] view plaincopy
  1. if Assigned(FOnActivate) then FOnActivate(Self);  
  2. CM_DEACTIVATE:  
  3. if Assigned(FOnDeactivate) then FOnDeactivate(Self);  
  4. CM_ENTER:  
  5. if not IsIconic(FHandle) and (GetFocus = FHandle) then  
  6. begin  
  7. TopWindow := FindTopMostWindow(0);  
  8. if TopWindow <> 0 then Windows.SetFocus(TopWindow);  
  9. end;  
  10. WM_HELP, // MessageBox(... MB_HELP)  
  11. CM_INVOKEHELP: InvokeHelp(WParam, LParam);  
  12. CM_WINDOWHOOK:  
  13. if wParam = 0 then  
  14. HookMainWindow(TWindowHook(Pointer(LParam)^)) else  
  15. UnhookMainWindow(TWindowHook(Pointer(LParam)^));  
  16. CM_DIALOGHANDLE:  
  17. if wParam = 1 then  
  18. Result := FDialogHandle  
  19. else  
  20. FDialogHandle := lParam;  
  21. WM_SETTINGCHANGE:  
  22. begin  
  23. Mouse.SettingChanged(wParam);  
  24. SettingChange(TWMSettingChange(Message));  
  25. Default;  
  26. end;  
  27. WM_FONTCHANGE:  
  28. begin  
  29. Screen.ResetFonts;  
  30. Default;  
  31. end;  
  32. WM_NULL:  
  33. CheckSynchronize;  
  34. else  
  35. Default;  
  36. end;  
  37. except  
  38. HandleException(Self);  
  39. end;  
  40. end;  
整个WndProc()方法,基本上只包含了一个庞大的case分支,其中给出了每个消息的处理代码,“WM_”打头的为Windows定义的窗口消息,“CM_”打头的为VCL库自定义的消息。
需要注意的是,这里给出WndProc是属于TApplication的,也就是那个0×0大小的Application窗口的窗口函数,而每个Form另外都有自己的窗口函数。
至此,读者应该清楚了VCL框架是如何封装Windows程序框架的了。知道VCL为我们做了什么,它想要提供给我们的是怎样的一个世界,这对于我们更好地融入VCL是大有好处的。这比从RAD角度看待VCL,有了更深一层的理解。好了,关于VCL和消息的话题到此为止。
0 0