使用資源—圖標和光標
圖標和光標是圖形資源,圖標通常用做應用程序的“形象代表”出現在文件瀏覽器、運行窗口左上角或程序的快捷方式等所有代表文件的地方,為自己寫的應用程序選一個合適的圖標會使程序變得引人注目;而光標就是鼠標移動時屏幕上那個指示位置的東西,應用程序可以定義自己的光標,這樣光標移到程序的客戶區中就會變成需要的形狀。
圖標和光標的資源定義
和菜單、加速鍵等資源不同,在資源腳本文件中定義圖標和光標時并不是一個個像素地定義,而是指定圖標和光標的文件名,由資源編譯器將像素數據讀入再轉換成二進制格式,所以在資源定義之前要用其他工具先創建圖標和光標文件。圖標和靜態光標文件的擴展名分別是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