news 2026/6/10 18:21:33

汇编语言全接触-23.系统托盘中的快捷图标

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
汇编语言全接触-23.系统托盘中的快捷图标

本课中,我们将学习如何把小图标放到系统托盘中去以及如何创建和使用弹出式菜单。

理论:

系统托盘是指任务条中的一个方形区域,在该区域中可以放入一些小图标,通常您可以在此处看到系统提供的最新时间。您自己当然也可以把快捷小图标放到此处。下面是这么做的步骤:

设置NOTIFYICONDATA型的结构体变量的成员变量的值:

cbSize 该结构体的大小。

hwnd 窗口的句柄。当鼠标滑过该小图标时,该窗口将接收到相关的消息。

uID 小图标的ID号。您可以取任意值,只是当您的应用程序有不止一个小图标时,您要能够区分出到底是那一个小图标接收到了鼠标的消息,也即ID号必须唯一。

uFlags 指定该结构体变量的那些成员变量有效。

NIF_ICON 有效。

NIF_MESSAGE 有效。

NIF_TIP 有效。

uCallbackMessage 自定义的消息。当鼠标对小图标动作时,WINDOWS外壳将把该消息发送到您的应用程序。该消息的值您可以自己定义。

hIcon 放入系统托盘中的图标的句柄。

szTip 64字节的缓冲区,它用来放入提示字符串,当鼠标停留在小图标上时,就会显示该字符串。

调用Shell_NotifyIcon函数。该函数在shell32.inc中定义,其原型如下:

Shell_NotifyIcon PROTO dwMessage:DWORD ,pnid:DWORD

dwMessage 是发送到WINDOWS外壳的消息:

NIM_ADD 把小图标加到系统托盘区。

NIM_DELETE 从系统托盘中删除小图标。

NIM_MODIFY 修改小图标。

pnid 是指向NOTIFYICONDATA型结构体变量的指针。

如果您想要加入一个小图标就用NIM_ADD,删除时使用NIM_DELETE消息。

基本上的消息就是这些。但是大多数的情况下,您不会仅仅满足把一个小图标放到那里。您还必须要对鼠标事件作出适当的反应。您可以在NOTIFYICONDATA型的结构体变量的成员变量uCallbackMessage 中设置您要处理的消息,然后WINDOWS外壳将在发生这些事件时通知您的应用程序。随着消息传送的参数wParam和lParam的值如下:

wParam 小图标的ID号。它和您在NOTIFYICONDATA型结构体变量中的成员变量uID中设置的值一样。

lParam 低字包含鼠标消息。譬如,用户在小图标上按下了右键时,lParam中将包含WM_RBUTTONDOWN消息。

大多数的系统托盘中的小图标,在用户用鼠标右击时都会弹出一个菜单以方便用户选择。我们可先创建菜单,然后调用TrackPopupMenu函数来显示它。步骤如下:

调用CreatePopupMenu函数来创建菜单。该函数创建一个空的菜单。如果成功,将在eax中返回该菜单的句柄。

调用AppendMenu, InsertMenu 或 InsertMenuItem来向菜单中加入菜单项。

当您想在当前鼠标位置显示该菜单时,调用GetCursorPosition函数来得到鼠标当前的屏幕位置,然后调用TrackPopupMenu来显示菜单。当用户从弹出式菜单中选择了一个菜单项时,WINDOWS将发送WM_COMMAND消息给您应用程序的消息处理过程,这和通常的菜单选择是一样的。.

注意:当您使用系统托盘中的小图标时有两件比较讨厌的事:

该菜单可能不会像通常那样马上消失掉。这是因为从弹出式接收消息的窗口必须是前景窗口。调用SetForegroundWindow函数就可以纠正该错误;

在调用了SetForegroundWindow函数后,您会发现第一次该弹出式菜单会正常弹出而且工作的很好。但是随后,该菜单只是一弹出就立即消失。根据MSDN,这么做是故意的。为了使得弹出菜单保持住,必须要求下一个切换到的是程序的主窗口。您可以通过邮寄任何消息给该程序的窗口来强行进行任务切换。注意要使用PostMessage而不是SendMessage。

例子:

.386

.model flat,stdcall

option casemap:none

include \masm32\include\windows.inc

include \masm32\include\user32.inc

include \masm32\include\kernel32.inc

include \masm32\include\shell32.inc

includelib \masm32\lib\user32.lib

includelib \masm32\lib\kernel32.lib

includelib \masm32\lib\shell32.lib

WM_SHELLNOTIFY equ WM_USER+5

IDI_TRAY equ 0

IDM_RESTORE equ 1000

IDM_EXIT equ 1010

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD

.data

ClassName db "TrayIconWinClass",0

AppName db "TrayIcon Demo",0

RestoreString db "&Restore",0

ExitString db "E&xit Program",0

.data?

hInstance dd ?

note NOTIFYICONDATA <>

hPopupMenu dd ?

.code

start:

invoke GetModuleHandle, NULL

mov hInstance,eax

invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT

invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD

LOCAL wc:WNDCLASSEX

LOCAL msg:MSG

LOCAL hwnd:HWND

mov wc.cbSize,SIZEOF WNDCLASSEX

mov wc.style, CS_HREDRAW or CS_VREDRAW or CS_DBLCLKS

mov wc.lpfnWndProc, OFFSET WndProc

mov wc.cbClsExtra,NULL

mov wc.cbWndExtra,NULL

push hInst

pop wc.hInstance

mov wc.hbrBackground,COLOR_APPWORKSPACE

mov wc.lpszMenuName,NULL

mov wc.lpszClassName,OFFSET ClassName

invoke LoadIcon,NULL,IDI_APPLICATION

mov wc.hIcon,eax

mov wc.hIconSm,eax

invoke LoadCursor,NULL,IDC_ARROW

mov wc.hCursor,eax

invoke RegisterClassEx, addr wc

invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\

WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\

CW_USEDEFAULT,350,200,NULL,NULL,\

hInst,NULL

mov hwnd,eax

.while TRUE

invoke GetMessage, ADDR msg,NULL,0,0

.BREAK .IF (!eax)

invoke TranslateMessage, ADDR msg

invoke DispatchMessage, ADDR msg

.endw

mov eax,msg.wParam

ret

WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

LOCAL pt:POINT

.if uMsg==WM_CREATE

invoke CreatePopupMenu

mov hPopupMenu,eax

invoke AppendMenu,hPopupMenu,MF_STRING,IDM_RESTORE,addr RestoreString

invoke AppendMenu,hPopupMenu,MF_STRING,IDM_EXIT,addr ExitString

.elseif uMsg==WM_DESTROY

invoke DestroyMenu,hPopupMenu

invoke PostQuitMessage,NULL

.elseif uMsg==WM_SIZE

.if wParam==SIZE_MINIMIZED

mov note.cbSize,sizeof NOTIFYICONDATA

push hWnd

pop note.hwnd

mov note.uID,IDI_TRAY

mov note.uFlags,NIF_ICON+NIF_MESSAGE+NIF_TIP

mov note.uCallbackMessage,WM_SHELLNOTIFY

invoke LoadIcon,NULL,IDI_WINLOGO

mov note.hIcon,eax

invoke lstrcpy,addr note.szTip,addr AppName

invoke ShowWindow,hWnd,SW_HIDE

invoke Shell_NotifyIcon,NIM_ADD,addr note

.endif

.elseif uMsg==WM_COMMAND

.if lParam==0

invoke Shell_NotifyIcon,NIM_DELETE,addr note

mov eax,wParam

.if ax==IDM_RESTORE

invoke ShowWindow,hWnd,SW_RESTORE

.else

invoke DestroyWindow,hWnd

.endif

.endif

.elseif uMsg==WM_SHELLNOTIFY

.if wParam==IDI_TRAY

.if lParam==WM_RBUTTONDOWN

invoke GetCursorPos,addr pt

invoke SetForegroundWindow,hWnd

invoke TrackPopupMenu,hPopupMenu,TPM_RIGHTALIGN,pt.x,pt.y,NULL,hWnd,NULL

invoke PostMessage,hWnd,WM_NULL,0,0

.elseif lParam==WM_LBUTTONDBLCLK

invoke SendMessage,hWnd,WM_COMMAND,IDM_RESTORE,0

.endif

.endif

.else

invoke DefWindowProc,hWnd,uMsg,wParam,lParam

ret

.endif

xor eax,eax

ret

WndProc endp

end start

分析:

该程序将显示一个简单的窗口。当您按下最小化按钮时,该窗口将隐藏,然后放一个小图标到系统托盘中。当您双击小图标时,应用程序将恢复自己,并把小图标从系统托盘中删除。当您右击小图标时,会显示一个弹出式菜单。您可以在菜单中选择是恢复窗口还是退出应用程序。

.if uMsg==WM_CREATE

invoke CreatePopupMenu

mov hPopupMenu,eax

invoke AppendMenu,hPopupMenu,MF_STRING,IDM_RESTORE,addr RestoreString

invoke AppendMenu,hPopupMenu,MF_STRING,IDM_EXIT,addr ExitString

当主窗口创建时,将会创建一个弹出式菜单,并且加入两个菜单项。 AppendMenu的语法如下:

AppendMenu PROTO hMenu:DWORD, uFlags:DWORD, uIDNewItem:DWORD, lpNewItem:DWORD

hMenu 是将要加入菜单项的菜单的句柄。

uFlags 告诉WINDOWS要加入的菜单项是位图、字符串或自画的项目以及是可用、不可用或灰色显示等。您可以从WIN32 API 指南中得到全部的标志位的信息。在我们的例子中使用标志位MF_STRING,它表示我们加入的菜单项是字符串。

uIDNewItem 是菜单项的ID号。这是一个用户自定义的值,它用来唯一地代表菜单项。.

lpNewItem 用来指定菜单项的内容,具体代表什么取决于uFlags中指定的标志。我们前面指定了MF_STRING标志,所以此处代表一个字符串

主窗口创建完成后,用户就可以开始测试了。这时按下最小化键。

当一个窗口被最小化时将接收到WM_SIZE消息,其中wParam参数中的值为SIZE_MINIMIZED。

.elseif uMsg==WM_SIZE

.if wParam==SIZE_MINIMIZED

mov note.cbSize,sizeof NOTIFYICONDATA

push hWnd

pop note.hwnd

mov note.uID,IDI_TRAY

mov note.uFlags,NIF_ICON+NIF_MESSAGE+NIF_TIP

mov note.uCallbackMessage,WM_SHELLNOTIFY

invoke LoadIcon,NULL,IDI_WINLOGO

mov note.hIcon,eax

invoke lstrcpy,addr note.szTip,addr AppNam

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 21:13:31

RepRapFirmware开源固件:3D打印机的智能控制核心终极指南

RepRapFirmware开源固件&#xff1a;3D打印机的智能控制核心终极指南 【免费下载链接】RepRapFirmware OO C RepRap Firmware 项目地址: https://gitcode.com/gh_mirrors/re/RepRapFirmware 在当今蓬勃发展的3D打印领域&#xff0c;一个高效稳定的控制固件是实现高质量打…

作者头像 李华
网站建设 2026/6/10 1:10:58

鸿蒙投屏神器HOScrcpy:零基础快速上手完整教程

鸿蒙投屏神器HOScrcpy&#xff1a;零基础快速上手完整教程 【免费下载链接】鸿蒙远程真机工具 该工具主要提供鸿蒙系统下基于视频流的投屏功能&#xff0c;帧率基本持平真机帧率&#xff0c;达到远程真机的效果。 项目地址: https://gitcode.com/OpenHarmonyToolkitsPlaza/HO…

作者头像 李华
网站建设 2026/6/10 12:19:17

18、Linux系统磁盘使用查询与软件安装管理全攻略

Linux系统磁盘使用查询与软件安装管理全攻略 1. 磁盘使用查询 在Linux系统中,有时候我们只需要知道某个目录的总使用空间,而不需要其所有子目录的详细信息。这时,可以使用 du 命令结合 -s 选项来实现。例如: $ cd music $ du -hs 2.6G .这里, du -hs 命令简洁…

作者头像 李华
网站建设 2026/6/10 1:00:00

【Redis从入门到精通,看这一篇就够了!】

在当今的后端开发领域&#xff0c;Redis绝对是一个绕不开的“明星中间件”。它以超高的性能、丰富的数据类型和灵活的使用场景&#xff0c;成为缓存、分布式锁、消息队列等场景的首选方案。很多小白在接触Redis时&#xff0c;会被“集群”“持久化”“红锁”这些概念吓倒&#…

作者头像 李华
网站建设 2026/6/10 18:16:59

重绘和重排怎么触发?怎么优化?

重绘&#xff08;Repaint&#xff09; 定义&#xff1a;元素样式改变但不影响布局时触发&#xff0c;仅重新绘制元素外观&#xff0c;不改变DOM几何结构。常见场景&#xff1a;修改color、background-color、opacity、box-shadow等。 重排&#xff08;Reflow&#xff09; 定义&…

作者头像 李华
网站建设 2026/6/10 12:23:22

[Java 并发编程] 线程池

线程池 1. 初识线程池 ​ 我们之所以引入线程&#xff0c;是因为进程的创建和销毁过于重量&#xff0c;而线程可以共享更多内存资源&#xff0c;因此成为显著提高效率的手段。但线程也是 OS 分配的&#xff0c;也涉及用户态和内核态的切换&#xff0c;也是一种很有限的资源&a…

作者头像 李华