使用资源—图标和光标
图标和光标是图形资源,图标通常用做应用程序的“形象代表”出现在文件浏览器、运行窗口左上角或程序的快捷方式等所有代表文件的地方,为自己写的应用程序选一个合适的图标会使程序变得引人注目;而光标就是鼠标移动时屏幕上那个指示位置的东西,应用程序可以定义自己的光标,这样光标移到程序的客户区中就会变成需要的形状。
图标和光标的资源定义
和菜单、加速键等资源不同,在资源脚本文件中定义图标和光标时并不是一个个像素地定义,而是指定图标和光标的文件名,由资源编译器将像素数据读入再转换成二进制格式,所以在资源定义之前要用其他工具先创建图标和光标文件。图标和静态光标文件的扩展名分别是ico和cur,还有一种扩展名为ani的动态光标文件。
光标和图标在资源文件中的定义语句是:
图标ID ICON [DISCARDABLE] 图标文件名 ;定义图标
光标ID CURSOR [DISCARDABLE] 光标文件名 ;定义光标
DISCARDABLE关键字是内存选项,表示在不用的时候可以从内存暂时卸掉,当文件名包含空格时,两边要用双引号引起来,图标ID和光标ID同样也可以用16位的整数或字符串表示,这里是几个定义的例子:
MyIcon icon “1.ico” ;把1.ico定义为ID为 “MyIcon”的图标资源
1000 icon discardable 2.ico ;把2.ico定义为ID为1000的图标资源
1001 icon “big icon.ico” ;把big icon.ico定义为ID为1001的图标资源
1002 cursor “big arrow.ani” ;把big arrow.ani定义为ID为1002的光标资源
GoodCursor cursor arrow.cur ;把arrow.cur定义为ID为“GoodCursor”的光标资源
注意:资源文件中定义的图标可以不止一个,但Windows在“我的电脑”中列出文件的时候总是使用资源中的第一个图标当做文件的图标,所以在资源脚本文件中要把想用做程序图标的图标定义语句排在最前面。
使用图标和光标
在这里,用一个例子来说明图标和光标的用法。
资源文件Icon.rc的定义如下:
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#include <resource.h>
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#define ICO_BIG 0x1000
#define ICO_SMALL 0x1001
#define CUR_2 0x1000
#define IDM_MAIN 0x2000
#define IDM_EXIT 0x2101
#define IDM_BIG 0x2201
#define IDM_SMALL 0x2202
#define IDM_CUR1 0x2203
#define IDM_CUR2 0x2204
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_SMALL ICON "Small.ico"
ICO_BIG ICON "Big.ico"
CUR_2 CURSOR "2.cur"
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
IDM_MAIN MENU DISCARDABLE
BEGIN
popup "文件(&F)"
BEGIN
menuitem "退出(&X)", IDM_EXIT
END
popup "图标和光标(&I)"
BEGIN
menuitem "大图标(&G)", IDM_BIG
menuitem "小图标(&M)", IDM_SMALL
menuitem separator
menuitem "光标A(&A)", IDM_CUR1
menuitem "光标B(&B)", IDM_CUR2
END
END
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
经过上一节的“洗礼”,读者对菜单的定义应该很熟悉了,这里就不再说明IDM_MAIN的定义了,脚本文件中定义ICO_SMALL,ICO_BIG两套图标和CUR_2静态光标,磁盘上还有个动态光标文件1.ani。
Icon.asm的大部分是窗口模板程序的内容,和FirstWindow.asm是相同的,仅在窗口过程的WM_CREATE和WM_COMMAND增加了一些内容:
.if eax == WM_CREATE
invoke LoadIcon, hInstance, ICO_BIG
mov hIcoBig, eax
invoke LoadIcon, hInstance, ICO_SMALL
mov hIcoSmall, eax
invoke LoadCursorFromFile, addr szCursorFile
mov hCur1, eax
invoke LoadCursor, hInstance, CUR_2
mov hCur2, eax
invoke SendMessage, hWnd, WM_COMMAND, IDM_BIG, NULL
invoke SendMessage, hWnd, WM_COMMAND, IDM_CUR1, NULL
;*************************************************************************
.elseif eax == WM_COMMAND
mov eax, wParam
movzx eax, ax
.if eax == IDM_EXIT
call _Quit
.elseif eax == IDM_BIG
invoke SendMessage, hWnd, WM_SETICON, ICON_BIG, hIcoBig
invoke CheckMenuRadioItem, hMenu, IDM_BIG, IDM_SMALL, IDM_BIG, MF_BYCOMMAND
.elseif eax == IDM_SMALL
invoke SendMessage, hWnd, WM_SETICON, ICON_BIG, hIcoSmall
invoke CheckMenuRadioItem, hMenu, IDM_BIG, IDM_SMALL, IDM_SMALL, MF_BYCOMMAND
.elseif eax == IDM_CUR1
invoke SetClassLong, hWnd, GCL_HCURSOR, hCur1
invoke CheckMenuRadioItem, hMenu, IDM_CUR1, IDM_CUR2, IDM_CUR1, MF_BYCOMMAND
.elseif eax == IDM_CUR2
invoke SetClassLong, hWnd, GCL_HCURSOR, hCur2
invoke CheckMenuRadioItem, hMenu, IDM_CUR1, IDM_CUR2, IDM_CUR2, MF_BYCOMMAND
.endif
1、装入图标和光标
在WM_CREATE消息中,程序从资源节区中装入所有的图标和光标资源,装入图标是用LoadIcon函数来完成的:
invoke LoadIcon, hInstance, lpIconName
.if eax
mov hIcon, eax
.endif
hInstance参数指定实例句柄,表示图标资源定义在哪个可执行文件中,lpIconName参数指定图标资源的名称,它就是资源文件中定义的图标ID值,如果调用成功的话,函数返回图标句柄。
除了可以装入资源文件中定义的图标资源之外,当参数hInstance为NULL的时候,用LoadIcon还可以用预定义的lpIconName参数装入Windows预定义的图标,参数说明如下表所示:
LoadIcon可以装入的预定义图标
lpIconName参数的预定义值
|
图标形状
|
IDI_APPLICATION
|
应用程序默认图标
|
IDI_ASTERISK
|
|
IDI_EXCLAMATION
|
警告图标(黄色三角形+感叹号)
|
IDI_HAND
|
严重警告图标
|
IDI_QUESTION
|
问号图标
|
IDI_WINLOGO
|
Window标徽图标
|
装入光标的函数有两个。装入的资源中定义的光标的函数是LoadCursor,它的语法和LoadIcon几乎一样:
invoke LoadCursor, hInstance, lpCursorName
.if eax
mov hCursor, eax
.endif
LoadCursor的用法也和LoadIcon相似,lpCursorName是光标资源的ID,LoadCursor也可以用指定hInstance为NULL的办法装入下表所列的预定义光标,这时候lpCursorname参数的取值如下表所示:
LoadCursor可以装入的预定义光标
预定义值
|
光标形状
|
IDC_APPSTARTING
|
标准的箭头形状国上小沙漏
|
IDC_ARROW
|
标准的箭头形状
|
IDC_CROSS
|
十字型光标
|
IDC_IBEAM
|
|
IDC_NO
|
禁止光标(圆圈里面国一个斜杠)
|
IDC_SIZE
|
改变大小的十字箭头
|
IDC_SIZENESW
|
东北和西南方向的双向箭头
|
IDC_SIZENS
|
向北和向南的双向箭头
|
IDC_SIZENWSE
|
西北和东南方向的双向箭头
|
IDC_SIZEWE
|
向西和向东的双向箭头
|
IDC_UPARROW
|
垂直箭头光标
|
IDC_WAIT
|
沙漏光标
|
注意:读者可以注意到,预定义的图标和光标都是Windows系统中常用的,预定义图标常用在消息框中,预定义光标就是Windows鼠标属性中的光标。使用预定义图标和光标的好处是它们的形状会随着系统设置值的不同自动改变,如改变“控制面板”->“鼠标”->“指针”中的设置后,装入的光标会自动改变。
另一个光标装入函数是LoadCursorFromFile,这个函数从磁盘光标文件中装入光标:
invoke LoadCursorFromFile, lpCursorFileName
.if eax
mov hCursor, eax
.endif
在Windows 9.x中,静态光标文件*.cur既可以定义在资源文件中,也可以使用LoadCursorFromFile函数装入,但是动态光标文件*.ani只能通过文件方式装入。在Windows 2000及XP中,两种光标文件都可以通过资源装入。为了在不同的操作系统上都可以使用,例子文件使用LoadCursorFromFile函数来装入动态光标文件。
2、使用图标和光标
现在来看如何使用图标。图标一般使用在对话框中或者程序窗口的标题栏中,要在标题栏中设置图标可以用向窗口发送WM_SETICON消息的办法实现:
invoke SendMessage, hWnd, WM_SETICON, ICON_BIG, hIcon
消息的wParam参数可以是ICON_BIG或ICON_SMALL,用来指定图标的分辨率为32x32还是16x16。
要将窗口的光标设置为新的光标不能使用WM_SETCURSOR,这个消息是通知窗口重新刷新光标而不是让它设定指定的光标。Windows中有个SetCursor函数可以用来设置窗口光标,但这只能将新的光标维持很短一段时间,因为当Windows向窗口重新发送WM_SETCURSOR消息的时候,光标就会被设置为原来的样子,WM_SETCURSOR是最频繁的消息之一,所以SetCursor并不能用来永久地改变窗口的光标。
如果要改变窗口的光标,正确的办法是用SetClassLong函数改变窗口类的属性,这个函数的使用方法如下:
invoke SetClassLong, hWnd, nIndex, dwNewLong
这个函数用来改变窗口类的属性,所以可以改变类中的光标设定,hWnd用来指定一个用这个类建立的某个窗口句柄,nIndex参数指定要改变窗口类的哪个属性,可以指定为GCL_HBRBACKGROUND, GCL_HCURSOR, GCL_HICON, GCL_HMODULE, GCL_MENUNAME, GCL_STYLE或GCL_WNDPROC等,它们分别表示要改变的窗口类的背景色、光标、图标、hInstance、菜单、风格或窗口过程地址,读者可以用这个函数来改变一个窗口类的几乎所有属性,程序中通过这个函数将窗口的光标在不同的光标句柄之间切换:
invoke SetClassLong, hWnd, GCL_HCURSOR, hCur1或hCur2
完整的Icon.asm代码如下:
.386
.model flat,stdcall
option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Equ 等值定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_BIG equ 1000h
ICO_SMALL equ 1001h
CUR_2 equ 1000h
IDM_MAIN equ 2000h
IDM_EXIT equ 2101h
IDM_BIG equ 2201h
IDM_SMALL equ 2202h
IDM_CUR1 equ 2203h
IDM_CUR2 equ 2204h
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?
hInstance dd ?
hWinMain dd ?
hMenu dd ?
hIcoBig dd ?
hIcoSmall dd ?
hCur1 dd ?
hCur2 dd ?
.const
szClassName db 'MyClass',0
szCaptionMain db 'Icon and Cursor Example',0
szCursorFile db '1.ani',0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 退出
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_Quit proc
invoke DestroyWindow, hWinMain
invoke PostQuitMessage, NULL
ret
_Quit endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 窗口过程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcWinMain proc uses ebx edi esi, hWnd, uMsg, wParam, lParam
mov eax,uMsg
;*************************************************************************
.if eax == WM_CREATE
invoke LoadIcon, hInstance, ICO_BIG
mov hIcoBig, eax
invoke LoadIcon, hInstance, ICO_SMALL
mov hIcoSmall, eax
invoke LoadCursorFromFile, addr szCursorFile
mov hCur1, eax
invoke LoadCursor, hInstance, CUR_2
mov hCur2, eax
invoke SendMessage, hWnd, WM_COMMAND, IDM_BIG, NULL
invoke SendMessage, hWnd, WM_COMMAND, IDM_CUR1, NULL
;*************************************************************************
.elseif eax == WM_COMMAND
mov eax, wParam
movzx eax, ax
.if eax == IDM_EXIT
call _Quit
.elseif eax == IDM_BIG
invoke SendMessage, hWnd, WM_SETICON, ICON_BIG, hIcoBig
invoke CheckMenuRadioItem, hMenu, IDM_BIG, IDM_SMALL, IDM_BIG, MF_BYCOMMAND
.elseif eax == IDM_SMALL
invoke SendMessage, hWnd, WM_SETICON, ICON_BIG, hIcoSmall
invoke CheckMenuRadioItem, hMenu, IDM_BIG, IDM_SMALL, IDM_SMALL, MF_BYCOMMAND
.elseif eax == IDM_CUR1
invoke SetClassLong, hWnd, GCL_HCURSOR, hCur1
invoke CheckMenuRadioItem, hMenu, IDM_CUR1, IDM_CUR2, IDM_CUR1, MF_BYCOMMAND
.elseif eax == IDM_CUR2
invoke SetClassLong, hWnd, GCL_HCURSOR, hCur2
invoke CheckMenuRadioItem, hMenu, IDM_CUR1, IDM_CUR2, IDM_CUR2, MF_BYCOMMAND
.endif
;***************************************************************************
.elseif eax == WM_CLOSE
call _Quit
;***************************************************************************
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
;***************************************************************************
xor eax,eax
ret
_ProcWinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WinMain proc
local @stWndClass:WNDCLASSEX
local @stMsg:MSG
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke LoadMenu, hInstance, IDM_MAIN
mov hMenu, eax
invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass
;**************************************************************************
; 注册窗口类
;**************************************************************************
push hInstance
pop @stWndClass.hInstance
mov @stWndClass.cbSize, sizeof WNDCLASSEX
mov @stWndClass.style, CS_HREDRAW or CS_VREDRAW
mov @stWndClass.lpfnWndProc, offset _ProcWinMain
mov @stWndClass.hbrBackground,COLOR_WINDOW + 1
mov @stWndClass.lpszClassName, offset szClassName
invoke RegisterClassEx, addr @stWndClass
;***************************************************************************
; 建立并显示窗口
;***************************************************************************
invoke CreateWindowEx, WS_EX_CLIENTEDGE, \
offset szClassName, offset szCaptionMain, \
WS_OVERLAPPEDWINDOW, \
100, 100, 400, 300, \
NULL, hMenu, hInstance, NULL
mov hWinMain,eax
invoke ShowWindow,hWinMain,SW_SHOWNORMAL
invoke UpdateWindow,hWinMain
;**************************************************************************
; 消息循环
;**************************************************************************
.while TRUE
invoke GetMessage, addr @stMsg, NULL, 0, 0
.break .if eax == 0
invoke TranslateMessage, addr @stMsg
invoke DispatchMessage, addr @stMsg
.endw
ret
_WinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
call _WinMain
invoke ExitProcess, NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start