posts - 2,  comments - 0,  trackbacks - 0
为了知道什么时候一个消息被发送到应用程序,必须用自己的窗口过程代替 Application的窗口过程。当在自己的窗口过程中对消息处理完后,要把消息再传递给原窗口过程。这样的过程就叫做子类化窗口。
可以传递一个常量GWL _ WNDPROC给Win32 API函数SetWindowLong( )来指定一个新的窗口过程。窗口过程可以是以下两种格式:一是利用API定义;二是利用Delphi使窗口方法作为窗口过程。注意 当子类化一个VCL窗口的窗口过程时,可能由于窗口的句柄被重复创建而导致应用程序失败。使用子类化技术一定要小心。一种更为安全的使用方法是使用 Application.HookMainWindow()。我们将在后面讨论它。

1. 一个Win32 API的窗口过程
一个API的窗口过程必须像这样声明:
1 function AWndProc (Handle:hWnd; Msg, wParam, lParam:Longint):Longint;stdcall;

声明中,Handle参数用于标识目标窗口;Msg是一个消息;wParam、lpParam参数含有消息的附加信息。函数的返回值要依靠收到的消息确定。需要特别注意,此函数必须用 stdcall作为调用约定。
可以这样使用SetWindowLong( )函数给应用程序的窗口指定窗口过程:
var
   WProc: Pointer;
begin
   WProc:
=Pointer(SetWindowLong(Application.Handle, GWL_WINDPROC, Integer(@NewWndProc)));

在此调用后,返回一个指针类型的WProc指向旧的窗口过程。对这个值的保留是很必要的,因为
有些消息可能需要传递给旧的窗口过程。下面是一个窗口过程的实现示例:
1 function AWndProc (Handle:hWnd; Msg, wParam, lParam:Longint):Longint;stdcall;
2 begin
3    Result:=CallWindowProc(WProc, Application.Handle, Msg, wParam, lParam);
4 end;
5 

ScWndPrc. pas单元的代码,程序中利用自己的窗口过程代替了 Application对象的窗口过程来处理自定义的消息DDG MFOOMSG。
unit ScWndPro;

 1 interface
 2 uses Forms, Messages;
 3 
 4 const DDGM_FOOMSG = WM_USER;
 5 
 6 var
 7   WProc: Pointer;
 8 
 9   function NewWndProc(Handle:hWnd; Msg, wParam, lParam:Longint):Logint;stdcall;
10   begin
11       if Msg = DDGM_FOOMSG then
12         showmessage('DDGM_FOOMSG new');
13 
14       Result:= CallWindowProc(WProc, Handle, Msg, wParam, lParam);
15   end;
16 
17 initialization
18   WProc := Pointer(SetWindowLong(Application.Handle, gwl_WndProc, Integer(@NewWndProc)));
19 
20   end.


警告 一定要把SetWindowLong( )函数值保存起来。如果你在自定义的窗口过程中不把该值返还给旧窗口过程,有可能导致应用程序甚至操作系统的崩溃。


2. Delphi的窗口方法
利用Delphi提供的函数MakeObjectInstance()可以把一个API窗口过程与一个Delphi方法关联。
MakeObjectInstance()能够创建一个TWndMethod类型的方法,该方法可以当作窗口过程使用。
MakeObjectInstance()在Forms单元中声明如下:
function MakeObjectInstance(Method: TWndMethod): Pointer;

TWndMethod在Forms单元中定义如下:
type
   TWndMethod 
= procedure (var Message: TMessage) of object;

MakeObjectInstance()的返回值为一个指针即Pointer,它指向新创建的窗口过程。这是SetWindowLong()需要的最后一个参数的值。最后,你要利用FreeObjectInstance()函数释放用MakeObjectInstance()创建的窗口方法。
作为一个示范程序,WinProc.dpr演示了子类化应用程序的窗口过程和如何利用Application.OnMessage的方法。
 1 unit Unit1;
 2 
 3 interface
 4 uses
 5   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 6   Dialogs, StdCtrls;
 7 
 8 type
 9   TForm1 = class(TForm)
10     btnSend: TButton;
11     btnPost: TButton;
12     procedure FormCreate(Sender: TObject);
13     procedure FormDestroy(Sender: TObject);
14     procedure btnSendClick(Sender: TObject);
15     procedure btnPostClick(Sender: TObject);
16   private
17     OldWndProc: Pointer;
18     WndProcPtr: Pointer;
19     procedure WndMethod(var Msg:TMessage);
20     procedure HandleAppMessage(var Msg:TMsg; var Handle: Boolean);
21   public
22     { Public declarations }
23   end;
24 
25 var
26   Form1: TForm1;
27 
28 implementation
29 
30 {$R *.dfm}
31 
32 procedure TForm1.btnPostClick(Sender: TObject);
33 begin
34    PostMessage(Application.Handle, DDGM_FOOMSG, 0,0);
35 end;
36 
37 procedure TForm1.btnSendClick(Sender: TObject);
38 begin
39   SendMessage(Application.Handle, DDGM_FOOMSG, 0,0);
40 end;
41 
42 procedure TForm1.FormCreate(Sender: TObject);
43 begin
44   Application.OnMessage := HandleAppMessage;
45   WndProcPtr := MakeObjectInstance(WndMethod);
46   OldWndProc := Pointer(SetWindowLong(Application.Handle, GWL_WNDPROC, integer(WndProcPtr)));
47 end;
48 
49 procedure TForm1.FormDestroy(Sender: TObject);
50 begin
51     SetWindowLong(Application.Handle,GWL_WNDPROC, Longint(OldWndProc));
52     FreeObjectInstance(WndProcPtr);
53 end;
54 
55 procedure TForm1.HandleAppMessage(var Msg: TMsg; var Handle: Boolean);
56 begin
57   if Msg.message = DDGM_FOOMSG then
58       Showmessage('DDGM_FOOMSG');
59 end;
60 
61 procedure TForm1.WndMethod(var Msg: TMessage);
62 begin
63   if Msg.Msg = DDGM_FOOMSG then
64       showmessage('New DDGM_FOOMSG');
65   with Msg do
66     result:= CallWindowProc(OldWndProc, Application.Handle, Msg, wParam, lParam);
67 end;
68 
69 end.


SendBtn按钮被按下时,API函数SendMessage()发送一个消息DDGM_FOOMSG给Application的窗
口句柄。当PostBtn按钮被按下时,同样的消息被PostMessage()API函数发送给Application。

HandleAppMessage()被指定来处理Application.OnMessage事件。HandleAppMessage()只是简单地
使用ShowMessage()显示一个消息框。OnMessage在主窗体的OnCreate事件处理过程中被指定。
注意在主窗体的OnDestroy事件处理过程中,要首先恢复应用程序原有的窗口过程,然后再通过调
用FreeObjectInstance()来释放由MakeProcInstance()创建的窗口过程。请注意,一定要先恢复再释放。
否则,会导致应用程序或操作系统被破坏。
可以看出,ScWndPrc单元被Main.pas引用。这意味着应用程序窗口被两次子类化一次是由
ScWndPrc单元使用API技术实现;另一次是在Main单元中使用窗口方法技术实现。注意:一定要牢记
在自定义的窗口过程和窗口方法中必须要用CallWindowProc()把消息传递给原窗口过程。
当运行此程序时,会看到无论哪一个按钮被按下,都有一个消息框被窗口过程或窗口方法引发。
但是,Application.OnMessage事件只能看到由PostMessage()函数发来的消息。
posted on 2011-11-20 17:52 firebird 阅读(470) 评论(0)  编辑 收藏 引用 所属分类: Delphi

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理