定制任务栏上的系统菜单 <br>云南昆明市 <br>吴中卫 <br>---- 通常,当我们在特定的场合或是在多应用的环境运行自己或其他的应用程序时,会在多窗口之间进行频繁的切换,以实施实时的交互得到所期望的结果,对于这样的“笨”办法,我们会不厌其烦地进行好多次,甚至是无数次。好在 Win 9x 在任务栏上显示了我们所运行或期望的程序,并配备了相应的右键系统弹出菜单,使我们在使用时方便多了。问题是那些都是缺省的系统菜单(命令),对于我们来说没有太大的帮助,下面我所介绍的就是利用 Win9x 为我们设定的任务栏系统菜单来达到快速、简捷的获取信息(执行命令)的目的。 <br><br>---- 提到系统菜单,就让我们联想到底层的 WIN32 系统调用:AppendMenu、Inert-Menu、ModifyMenu。仅此三个函数,就足可以让我们对缺省的系统菜单进行定制,应用自如了。为简单起见,本文就 AppendMenu 进行一些介绍,其余的请朋友们参见 WIN32API 帮助文件。 <br><br> 以下是 AppendMenu 函数原型: <br> BOOL AppendMenu(<br> HMENU hMenu, // 要定制的菜单句柄<br> UINT uFlags, // 怎样定制菜单项<br> UINT uIDNewItem, // 要定制的菜单项标识或子菜单句柄<br> LPCTSTR lpNewItem // 要定制的菜单项(字串)<br> );<br><br>---- 在 AppendMenu 函数里,lpNewItem 和 uIDNewItem 参数依赖 uFlags 的不同标志而有所变化,让我们来看一看 uFlags 到底有那一些定义值: <br><br>MF_BITMAP 指明该菜单项是一位图,在 lpNewItem 参数代表位图句柄<br>MF_CHECKED 在菜单项的前面放上一个“选中”标记<br>MF_DISABLED 屏蔽该菜单项,但不象平常那样变成灰色<br>MF_ENABLED 与 MF_DISABLED 相反<br>MF_GRAYED 除了有 MF_DISABLED 的作用以外,还把该菜单项变灰<br>MF_MENUBREAK 把该菜单与现有菜单并排放在一起<br>MF_MENUBARBREAK 与 MF_MENUBREAK 相同,除了在中间放一条竖线外<br>MF_OWNERDRAW 表明该菜单项为自绘菜单项,还必须处理一切的显示、更新<br> 问题<br>MF_POPUP 该菜单项为一子菜单,uIDNewItem 参数代表其句柄<br>MF_SEPARATOR 与上一菜单项画上一分割线,系统将忽略 lpNewItem 和 <br> uIDNewItem 参数<br>MF_STRING 该菜单项是一文本字串,lpNewItem 是其内容<br>MF_UNCHECKED 取消该菜单项前面的“选中”标记<br><br>---- 我们看到,除了 MF_POPUP,MF_BITMAP,MF_OWNERDRAW 以外,MF_STRING 是最常用的菜单项定义方法,uIDNewItem 在这里是命令消息的 wParam 参数。朋友们还记得获取普通菜单的句柄用 GetMenu, 那么获取系统菜单的句柄就应该用 GetSyst-emMenu。这时有一个问题:程序怎么知道该获取的系统菜单是窗口上的系统菜单,还是任务栏上的系统菜单呢?这就依靠传递给 GetSystemMenu 的是哪一个参数。因为我们当前考虑的是任务栏,因此这个参数就必须是 Application.Handle。 <br>---- 如果这时候就着手编制一个 Project 检验一下,朋友们就会发现...“我”定义的菜单怎么没有反应!原来该菜单项根本没有在 Application 里处理过。显然,我们还得过滤一下传到 Application 的 WM_SYSCOMMAND 消息。 <br><br>---- 为了加深了解,我们首先追加一个简单的任务栏系统菜单: <br><br> Unit AddMenuDemo;<br> interface<br><br>uses Windows, Messages, SysUtils, Classes, <br>Graphics, Controls, Forms,<br> Dialogs,Menus;<br><br> type<br> TDemoForm = class(TForm)<br> procedure FormCreate(Sender: TObject);<br> ...<br> private<br> { Private declarations }<br> // 我的消息过滤器<br> procedure OnAppMessage<br>(var Msg: TMsg; var Handled: Boolean);<br> ...<br> public<br> { Public declarations }<br> ...<br> end;<br><br> var<br> DemoForm1: TDemoForm;<br><br> implementation<br><br> const<br> My_SimpleCMD1 = WM_USER + 1; <br> // 定义三个用户消息,用来处理菜单项被点击<br> <br> // 时相应的动作<br> My_SimpleCMD2 = WM_USER + 2;<br> My_SimpleCMD3 = WM_USER + 3;<br> ...<br><br> procedure TDemoForm1.FormCreate(Sender: TObject);<br> begin<br> Application.OnMessage := OnAppMessage; <br> //定义自己的消息处理过程<br><br> //定义了提交、反馈、处理三个菜单项<br> AppendMenu(GetSystemMenu<br>(Application.Handle, FALSE),MF_STRING,<br> My_SimpleCMD1,'提交');<br> AppendMenu(GetSystemMenu<br>(Application.Handle, FALSE),MF_STRING,<br> My_SimpleCMD2,'反馈');<br> AppendMenu(GetSystemMenu<br>(Application.Handle, FALSE),MF_SEPARATOR,<br> 0, ''); // 定义一分割线<br> AppendMenu(GetSystemMenu<br>(Application.Handle, FALSE),MF_STRING,<br> My_SimpleCMD3,'处理');<br> end; <br><br>procedure TDemoForm1.OnAppMessage<br>(var Msg: TMsg; var Handled: Boolean);<br> begin<br> // 定制自己的消息处理过程<br> if (Msg.message = WM_SYSCOMMAND) and <br>(Msg.wParam = My_SimpleCMD1) then<br> begin<br> ShowMessage('正在提交...');<br> Handled := True;<br> ...<br> end;<br> if (Msg.message = WM_SYSCOMMAND) and <br>(Msg.wParam = My_SimpleCMD2) then<br> begin<br> ShowMessage('正在反馈...');<br> Handled := True;<br> ...<br> end;<br> if (Msg.message = WM_SYSCOMMAND) and <br>(Msg.wParam = My_SimpleCMD3) then<br> begin<br> ShowMessage('正在处理...');<br> Handled := True;<br> ...<br> end;<br> end;<br> ....<br><br>---- 当我们处理的事务比较多并且可分组,以上的做法就比较烦琐,这时就应该定义成多个 MF_POPUP ,其各个子菜单除了 uIDNewItem 传递这个子菜单的句柄外,另外独特之处在于消息过滤器的 Msg.wParam 是各个菜单项的 Command。这个 Command是在构建 TPopupMenu 时就定义了的。无论是否是 MenuItem 或 PopupMenu,其Command的值都是顺序递增的。以下是一范例: <br><br> unit Unit1;<br><br> interface<br><br> uses Windows, Messages, SysUtils, <br>Classes, Graphics, Controls, Forms, <br> Dialogs, Menus;<br><br> type<br> TForm1 = class(TForm)<br> PopupMenu1: TPopupMenu;<br> N1: TMenuItem; // Caption := '范例一';<br> N2: TMenuItem; // Caption := '范例二';<br> N3: TMenuItem; // Caption := '范例三';<br> procedure FormCreate(Sender: TObject);<br> ...<br> private<br> { Private declarations }<br> // 我的消息过滤器<br> procedure OnAppMessage<br>(var Msg: TMsg; var Handled: Boolean);<br> ...<br> public<br> { Public declarations }<br> ...<br> end;<br> ...<br><br> var<br> Form1: TForm1;<br><br> implementation<br> {$R *.DFM}<br><br> procedure TForm1.FormCreate(Sender: TObject);<br> begin<br> Application.OnMessage := OnAppMessage; <br> //定义自己的消息处理过程<br><br> // 定义一分割线 <br> AppendMenu(GetSystemMenu(Application.Handle,<br> FALSE), MF_SEPARATOR, 0, '');<br><br> // 定义'提交'子菜单<br> AppendMenu(GetSystemMenu<br>(Application.Handle, FALSE), MF_POPUP,<br> PopupMenu1.Handle,'提交');<br> end;<br><br>procedure TForm1.OnAppMessage<br>(var Msg: TMsg; var Handled: Boolean);<br> begin<br><br> // 定制自己的消息处理过程<br> if (Msg.message = WM_SYSCOMMAND) <br>AND (Msg.wParam < WM_USER) then<br> begin<br> Case Msg.wParam of<br> // N1.Command = 1<br> 1:ShowMessage<br>('范例一 Command:'+ IntToStr(N1.Command));<br> // N1.Command = 2<br> 2:ShowMessage<br>('范例二 Command:'+ IntToStr(N2.Command));<br> // N1.Command = 3<br> 3:ShowMessage<br>('范例三 Command:'+ IntToStr(N3.Command));<br> end;<br> Handled := True;<br> end;<br> ...<br> end;<br><br><br>---- 可以看出,MF_STRING 与 MF_POPUP 在消息处理机制上是稍微有点不同的,另外如果要在菜单前面加上图标的话,这在 Delphi 4.x、Delphi 5.x 版本下是不成问题,如果是在 Delphi 3.x 下的话,只要定义成自绘 MenuItem 即可,不过稍许复杂了一点。在应用过程中,需要提醒的是,这几组定义是不能一起使用: <br>* A. MF_DISABLED, MF_ENABLED, 和 MF_GRAYED <br>* B. MF_BITMAP, MF_STRING, 和 MF_OWNERDRAW <br>* C. MF_MENUBARBREAK 和 MF_MENUBREAK <br>* D. MF_CHECKED 和 MF_UNCHECKED <br><br>可能晚了