使用資源—圖標(biāo)和光標(biāo)
圖標(biāo)和光標(biāo)是圖形資源,圖標(biāo)通常用做應(yīng)用程序的“形象代表”出現(xiàn)在文件瀏覽器、運(yùn)行窗口左上角或程序的快捷方式等所有代表文件的地方,為自己寫的應(yīng)用程序選一個合適的圖標(biāo)會使程序變得引人注目;而光標(biāo)就是鼠標(biāo)移動時屏幕上那個指示位置的東西,應(yīng)用程序可以定義自己的光標(biāo),這樣光標(biāo)移到程序的客戶區(qū)中就會變成需要的形狀。
圖標(biāo)和光標(biāo)的資源定義
和菜單、加速鍵等資源不同,在資源腳本文件中定義圖標(biāo)和光標(biāo)時并不是一個個像素地定義,而是指定圖標(biāo)和光標(biāo)的文件名,由資源編譯器將像素數(shù)據(jù)讀入再轉(zhuǎn)換成二進(jìn)制格式,所以在資源定義之前要用其他工具先創(chuàng)建圖標(biāo)和光標(biāo)文件。圖標(biāo)和靜態(tài)光標(biāo)文件的擴(kuò)展名分別是ico和cur,還有一種擴(kuò)展名為ani的動態(tài)光標(biāo)文件。
光標(biāo)和圖標(biāo)在資源文件中的定義語句是:
圖標(biāo)ID ICON [DISCARDABLE] 圖標(biāo)文件名 ;定義圖標(biāo)
光標(biāo)ID CURSOR [DISCARDABLE] 光標(biāo)文件名 ;定義光標(biāo)
DISCARDABLE關(guān)鍵字是內(nèi)存選項(xiàng),表示在不用的時候可以從內(nèi)存暫時卸掉,當(dāng)文件名包含空格時,兩邊要用雙引號引起來,圖標(biāo)ID和光標(biāo)ID同樣也可以用16位的整數(shù)或字符串表示,這里是幾個定義的例子:
MyIcon icon “1.ico” ;把1.ico定義為ID為 “MyIcon”的圖標(biāo)資源
1000 icon discardable 2.ico ;把2.ico定義為ID為1000的圖標(biāo)資源
1001 icon “big icon.ico” ;把big icon.ico定義為ID為1001的圖標(biāo)資源
1002 cursor “big arrow.ani” ;把big arrow.ani定義為ID為1002的光標(biāo)資源
GoodCursor cursor arrow.cur ;把arrow.cur定義為ID為“GoodCursor”的光標(biāo)資源
注意:資源文件中定義的圖標(biāo)可以不止一個,但Windows在“我的電腦”中列出文件的時候總是使用資源中的第一個圖標(biāo)當(dāng)做文件的圖標(biāo),所以在資源腳本文件中要把想用做程序圖標(biāo)的圖標(biāo)定義語句排在最前面。
使用圖標(biāo)和光標(biāo)
在這里,用一個例子來說明圖標(biāo)和光標(biāo)的用法。
資源文件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 "圖標(biāo)和光標(biāo)(&I)"
BEGIN
menuitem "大圖標(biāo)(&G)", IDM_BIG
menuitem "小圖標(biāo)(&M)", IDM_SMALL
menuitem separator
menuitem "光標(biāo)A(&A)", IDM_CUR1
menuitem "光標(biāo)B(&B)", IDM_CUR2
END
END
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
經(jīng)過上一節(jié)的“洗禮”,讀者對菜單的定義應(yīng)該很熟悉了,這里就不再說明IDM_MAIN的定義了,腳本文件中定義ICO_SMALL,ICO_BIG兩套圖標(biāo)和CUR_2靜態(tài)光標(biāo),磁盤上還有個動態(tài)光標(biāo)文件1.ani。
Icon.asm的大部分是窗口模板程序的內(nèi)容,和FirstWindow.asm是相同的,僅在窗口過程的WM_CREATE和WM_COMMAND增加了一些內(nèi)容:
.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、裝入圖標(biāo)和光標(biāo)
在WM_CREATE消息中,程序從資源節(jié)區(qū)中裝入所有的圖標(biāo)和光標(biāo)資源,裝入圖標(biāo)是用LoadIcon函數(shù)來完成的:
invoke LoadIcon, hInstance, lpIconName
.if eax
mov hIcon, eax
.endif
hInstance參數(shù)指定實(shí)例句柄,表示圖標(biāo)資源定義在哪個可執(zhí)行文件中,lpIconName參數(shù)指定圖標(biāo)資源的名稱,它就是資源文件中定義的圖標(biāo)ID值,如果調(diào)用成功的話,函數(shù)返回圖標(biāo)句柄。
除了可以裝入資源文件中定義的圖標(biāo)資源之外,當(dāng)參數(shù)hInstance為NULL的時候,用LoadIcon還可以用預(yù)定義的lpIconName參數(shù)裝入Windows預(yù)定義的圖標(biāo),參數(shù)說明如下表所示:
LoadIcon可以裝入的預(yù)定義圖標(biāo)
lpIconName參數(shù)的預(yù)定義值
|
圖標(biāo)形狀
|
IDI_APPLICATION
|
應(yīng)用程序默認(rèn)圖標(biāo)
|
IDI_ASTERISK
|
|
IDI_EXCLAMATION
|
警告圖標(biāo)(黃色三角形+感嘆號)
|
IDI_HAND
|
嚴(yán)重警告圖標(biāo)
|
IDI_QUESTION
|
問號圖標(biāo)
|
IDI_WINLOGO
|
Window標(biāo)徽圖標(biāo)
|
裝入光標(biāo)的函數(shù)有兩個。裝入的資源中定義的光標(biāo)的函數(shù)是LoadCursor,它的語法和LoadIcon幾乎一樣:
invoke LoadCursor, hInstance, lpCursorName
.if eax
mov hCursor, eax
.endif
LoadCursor的用法也和LoadIcon相似,lpCursorName是光標(biāo)資源的ID,LoadCursor也可以用指定hInstance為NULL的辦法裝入下表所列的預(yù)定義光標(biāo),這時候lpCursorname參數(shù)的取值如下表所示:
LoadCursor可以裝入的預(yù)定義光標(biāo)
預(yù)定義值
|
光標(biāo)形狀
|
IDC_APPSTARTING
|
標(biāo)準(zhǔn)的箭頭形狀國上小沙漏
|
IDC_ARROW
|
標(biāo)準(zhǔn)的箭頭形狀
|
IDC_CROSS
|
十字型光標(biāo)
|
IDC_IBEAM
|
|
IDC_NO
|
禁止光標(biāo)(圓圈里面國一個斜杠)
|
IDC_SIZE
|
改變大小的十字箭頭
|
IDC_SIZENESW
|
東北和西南方向的雙向箭頭
|
IDC_SIZENS
|
向北和向南的雙向箭頭
|
IDC_SIZENWSE
|
西北和東南方向的雙向箭頭
|
IDC_SIZEWE
|
向西和向東的雙向箭頭
|
IDC_UPARROW
|
垂直箭頭光標(biāo)
|
IDC_WAIT
|
沙漏光標(biāo)
|
注意:讀者可以注意到,預(yù)定義的圖標(biāo)和光標(biāo)都是Windows系統(tǒng)中常用的,預(yù)定義圖標(biāo)常用在消息框中,預(yù)定義光標(biāo)就是Windows鼠標(biāo)屬性中的光標(biāo)。使用預(yù)定義圖標(biāo)和光標(biāo)的好處是它們的形狀會隨著系統(tǒng)設(shè)置值的不同自動改變,如改變“控制面板”->“鼠標(biāo)”->“指針”中的設(shè)置后,裝入的光標(biāo)會自動改變。
另一個光標(biāo)裝入函數(shù)是LoadCursorFromFile,這個函數(shù)從磁盤光標(biāo)文件中裝入光標(biāo):
invoke LoadCursorFromFile, lpCursorFileName
.if eax
mov hCursor, eax
.endif
在Windows 9.x中,靜態(tài)光標(biāo)文件*.cur既可以定義在資源文件中,也可以使用LoadCursorFromFile函數(shù)裝入,但是動態(tài)光標(biāo)文件*.ani只能通過文件方式裝入。在Windows 2000及XP中,兩種光標(biāo)文件都可以通過資源裝入。為了在不同的操作系統(tǒng)上都可以使用,例子文件使用LoadCursorFromFile函數(shù)來裝入動態(tài)光標(biāo)文件。
2、使用圖標(biāo)和光標(biāo)
現(xiàn)在來看如何使用圖標(biāo)。圖標(biāo)一般使用在對話框中或者程序窗口的標(biāo)題欄中,要在標(biāo)題欄中設(shè)置圖標(biāo)可以用向窗口發(fā)送WM_SETICON消息的辦法實(shí)現(xiàn):
invoke SendMessage, hWnd, WM_SETICON, ICON_BIG, hIcon
消息的wParam參數(shù)可以是ICON_BIG或ICON_SMALL,用來指定圖標(biāo)的分辨率為32x32還是16x16。
要將窗口的光標(biāo)設(shè)置為新的光標(biāo)不能使用WM_SETCURSOR,這個消息是通知窗口重新刷新光標(biāo)而不是讓它設(shè)定指定的光標(biāo)。Windows中有個SetCursor函數(shù)可以用來設(shè)置窗口光標(biāo),但這只能將新的光標(biāo)維持很短一段時間,因?yàn)楫?dāng)Windows向窗口重新發(fā)送WM_SETCURSOR消息的時候,光標(biāo)就會被設(shè)置為原來的樣子,WM_SETCURSOR是最頻繁的消息之一,所以SetCursor并不能用來永久地改變窗口的光標(biāo)。
如果要改變窗口的光標(biāo),正確的辦法是用SetClassLong函數(shù)改變窗口類的屬性,這個函數(shù)的使用方法如下:
invoke SetClassLong, hWnd, nIndex, dwNewLong
這個函數(shù)用來改變窗口類的屬性,所以可以改變類中的光標(biāo)設(shè)定,hWnd用來指定一個用這個類建立的某個窗口句柄,nIndex參數(shù)指定要改變窗口類的哪個屬性,可以指定為GCL_HBRBACKGROUND, GCL_HCURSOR, GCL_HICON, GCL_HMODULE, GCL_MENUNAME, GCL_STYLE或GCL_WNDPROC等,它們分別表示要改變的窗口類的背景色、光標(biāo)、圖標(biāo)、hInstance、菜單、風(fēng)格或窗口過程地址,讀者可以用這個函數(shù)來改變一個窗口類的幾乎所有屬性,程序中通過這個函數(shù)將窗口的光標(biāo)在不同的光標(biāo)句柄之間切換:
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
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 數(shù)據(jù)段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.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
;**************************************************************************
; 消息循環(huán)
;**************************************************************************
.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