首页  编辑  

关于任务栏菜单

Tags: /超级猛料/VCL/Menu.菜单/   Date Created:

关于任务栏的菜单

一个偏方:

procedure TForm1.btn1Click(Sender: TObject);

var

 P: PHandle;

begin

 P := @Application.Handle;

 P^ := Handle;

end;

---------------------------------------

{ 下面的属于Kingron原创,转载请注明出处,并保留版权信息 }

{ 本文未经许可,严禁转载 }

看了N个关于任务栏菜单的贴子,例如,有一个网友问如何做出和Winamp3.0的任务栏类似的菜单,也有的网友问如何拦截任务栏菜单的弹出消息,实际上,这个问题很简单的。下面是Kingron的研究结果!

我们都知道,在Delphi Application中,有一个隐含的Window,就是TApplication,这是一个隐藏的窗口,他在后面默默处理这一切关于Application相关的东西,我们甚至可以使用ShowWindow(Application.Handle,SW_SHOW)来看到这个窗口!这些虽然是题外话,但是,我们下面要做的却和这个窗口相关,因此你如果要继续的话,最好先补习一下子类化、TApplication、WndProc、Windows的消息机制等等相关的知识。我们知道对于一个Form,可以很容易拦截System Menu的消息,例如我们只要拦截WM_SYSCOMMAND就可以得到窗口系统菜单的消息,那么要拦截菜单的弹出消息,我们只要拦截WM_INITMENU即可!我们可以做一个简单的实验,New一个Application,然后在主窗体里面添加类似代码:

 private

   { Private declarations }

   procedure wmtest(var msg:TMessage);message wm_initmenu;

..........

procedure TForm1.wmtest(var msg: TMessage);

begin

 Caption:='InitMenu';

end;

然后运行程序,点击Sys Menu,你就会看到效果!到这里,我们已经发现,只要拦截WM_INITMENU消息,就可以知道系统菜单的弹出了!现在问题是,不过我怎么写代码,可是任务栏菜单的弹出对于WM_INITMENU的拦截没有任何反应!难道我们那里错了吗?难道还有其它的东西隐藏在后面?问题的关键在于我们拦截的窗口不对!对于任务栏的菜单而言,这个菜单就是Application窗口的System Menu!因此我们的关键在于要拦截Application窗口的WM_INITMENU消息!至此,我们已经前进了一大步!知道了这一点,就比较好办了,我们知道,Delphi中有一个TApplicationEvents控件,他有一个属性,就是OnMessage,因此我们只要把Application的消息都经过OnMessage处理不就可以了?看起来似乎是一个完美的方法,然而,但你在OnMessage添加类似的代码的时候,你会发现,根本没有任何作用:

procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;

 var Handled: Boolean);

begin

 if Msg.message = wm_initmenu then

   Caption:='OK';

end;

那个地方出现了错误?没有,哪里都没有,问题是,Delphi在封装的时候,过滤掉了一些东西,Application的OnMessage并不能处理程序的每一个消息,有些东西被他丢掉了!看来此路不通,必须另外找一个方法!然而幸运的是,我们都知道,在Windows的每一个窗口中,都有一个WndProc过程,这个过程负责处理"所有"的消息,因此我们只要Hook这个WndProc即可,也就是说我们只要把TApplication窗口的WndProc过程让我们接管,我们处理之后,然后仍然交给原来的WndProc处理即可,这样就可以达到我们的目的!那么如何来Hook这个Application窗口的WndProc过程呢?很简单,只要两个函数和几句简单的代码就可以了。必须用到的函数是GetWindowLong、SetWindowLong、CallWindowProc,首先我们用GetWindowLong获取原来的WndPro过程:

 OldWndProc := TFarProc(GetWindowLong(Application.Handle, GWL_WNDPROC));

其中OldWndProc就是用来保存原来的WndProc过程的,因为我们还需要他!

然后利用SetWindowLong来挂接我们的WndProc过程:

SetWindowLong(Application.Handle, GWL_WNDPROC,longint(@NewWndProc));

此处的NewWndProc就是我们自己的WndProc处理过程!类似下面:

function NewWndProc(hWndAppl: HWnd; Msg, wParam: Word; lParam: Longint): Longint; stdcall;

begin

 NewWndProc := 0; { Default WndProc return value }

 { * * * Handle messages here; The message number is in Msg * * * }

 case msg of

   WM_INITMENU:MessageBox(0,'OK','Info',MB_OK+MB_ICONINFORMATION);

 end;

 NewWndProc := CallWindowProc(OldWndProc, hWndAppl, Msg, wParam, lParam);

end;

至此,问题已经解决了,如果想简单一点儿的话,我们可以做一个简单的控件即可。

unit TaskMenu ;

{*************************************************************}

{*                                                           *}

{*      TaskMenu Control,Copyright Kingron 2002              *}

{*             All rights reserverd.                         *}

{*         Bug Report : Kingron@163.net                      *}

{*         WEB : http://kingron.myetang.com                  *}

{* Special Thank:ChongChong(http://www.lkgarden.com/lfpsoft) *)

{*                                                           *}

{*************************************************************}

interface

uses

 Windows , Messages , SysUtils , Classes , Menus , Forms , Controls ;

type

 TTaskMenu = class ( TPopupMenu )

  private

    { Private declarations }

   OldWndProc : Pointer ;

   NewWndProc : Pointer ;

  protected

    { Protected declarations }

    procedure HookWin ;

    procedure UnHookWin ;

    procedure Hooked ( var Msg : TMessage );

  public

    { Public declarations }

    constructor Create ( AOwner : TComponent ); override ;

    destructor Destroy ; override ;

  published

    { Published declarations }

  end ;

procedure Register ;

implementation

const

 CM_POPUP_MENU     = WM_USER + $500 ;

 CM_APP_MENU       = $0313 ;

procedure Register ;

begin

 RegisterComponents ( 'Samples' , [ TTaskMenu ]);

end ;

{ TTaskMenu }

procedure TTaskMenu . Hooked ( var Msg : TMessage );

begin

  case Msg . Msg of

   CM_APP_MENU : PostMessage ( Application . Handle , CM_POPUP_MENU , 0 , 0 );

   CM_POPUP_MENU : Popup ( Mouse . CursorPos . X , Mouse . CursorPos . Y );

  else

   Msg . Result := CallWindowProc ( OldWndProc , Application . Handle , Msg . Msg , Msg . wParam , Msg . lParam );

  end ;

end ;

constructor TTaskMenu . Create ( AOwner : TComponent );

begin

  inherited ;

 HookWin ;

end ;

destructor TTaskMenu . Destroy ;

begin

 UnHookWin ;

  inherited ;

end ;

procedure TTaskMenu . HookWin ;

begin

 OldWndProc := Pointer ( GetWindowLong ( Application . Handle , GWL_WNDPROC ));

 NewWndProc := Classes . MakeObjectInstance ( Hooked );

  if not ( csDesigning in ComponentState ) then

   SetWindowLong ( Application . Handle , GWL_WNDPROC , longint ( NewWndProc ));

end ;

procedure TTaskMenu . UnHookWin ;

begin

 SetWindowLong ( Application . Handle , GWL_WNDPROC , longint ( OldWndProc ));

  if Assigned ( NewWndProc ) then Classes . FreeObjectInstance ( NewWndProc );

 NewWndProc := nil ;

end ;

end .