屏幕保護程序是什麼,相信大家都用過,但對於它的結構也許就不那麼熟悉了。屏幕保護程序是一種特使的 .exe 文件,實際上它是一個標准的 PE 文件,除了有擴展名 .scr,當然這個擴展名也是用連接程序產生的 .exe 文件改名得到的。但在編程中,屏幕保護程序又有它的特殊的地方,說穿了就是它的編程規范。
屏幕保護程序有以下特點:
屏幕保護程序是Win32 API 支持一種特殊的應用程序並由系統自動激活。其機制是當條件滿足時,系統向當前活動窗口發出字參數 wParam 值為 SC_SCREENSAVE 的 WM_SYSCOMMAND 消息,然後由當前活動窗口執行 SYSTEM.INI 文件中 [boot] 區指定的屏幕保護程序。
屏幕保護程序激活的條件是:在規定時間內沒有鼠標或鍵盤輸入、當前的活動窗口是標准的 WINDOWS 應用程序。因為非 WINDOWS 應用,不會理睬 WM_SYSCOMMAND 消息。顯然,如果當前活動的程序接管了字參數 wParam 值為 SC_SCREENSAVE 的 WM_SYSCOMMAND 消息並且不傳遞到 DefWindowProc 函數就可以禁止屏幕保護程序。這對某些運行中不願意被打斷的程序如視頻播放,光盤刻錄程序特別有用。
可以在控制面板的顯示器中選擇需要的屏幕保護程序,並可以配置屏幕保護程序的參數。配置的對話框由屏幕保護程序提供。
下面是編寫屏幕保護程序的要點:
屏幕保護程序的編寫由靜態鏈接庫 SCRNSAVE.LIB 支持,它包含了建立屏幕保護程序的主程序和缺省功能,如建立一個缺省的大小為全屏幕的窗口供用戶使用,並提供缺省的消息處理程序,它對下面消息的缺省處理是:
WM_SETCURSOR -- 將光標設置為無
WM_PAINT -- 畫屏幕背景
WM_LBUTTONDOWN、WM_MBUTTONDOWN、WM_RBUTTONDOWN、WM_KEYDOWN、WM_MOUSEMOVE -- 終止執行
WM_ACTIVATE -- 如果 wParam 是 FALSE,則終止執行
程序的入口代碼已經包括在 scrnsave.lib 中,名稱為 WinMain,所以程序尾包括 end WinMain 即可。
用戶只需編寫三個基本函數必須名為 ScreenSaverConfigureDialog、ScreenSaverProc 和 RegisterDialogClasses,這 3 個函數必須在.DEF 文件中指定 export
ScreenSaverProc - 主過程,也就是自動建立的主窗口的窗口過程,所有對屏幕的處理就是由它完成的。可以把未處理的消息傳遞到 DefScreenSaverProc函數,由系統處理上面說到的缺省處理。缺省 DefScreenSaverProc 過程處理 WM_LBUTTONDOWN、WM_MBUTTONDOWN ; WM_RBUTTONDOWN、WM_KEYDOWN、WM_MOUSEMOVE 消息並結束程序,如果在這些消息時不想退出,可以自行處理,不要傳遞到 DefScreenSaverProc。
ScreenSaverConfigureDialog - 處理屏幕保護程序配置對話框過程,這個過程並不是由主程序調用的,而是由控制面板的顯示器設置程序調用。用戶輸入的配置數據應該輸出到.INI 或注冊表中。
RegisterDialogClasses - 登記屏幕保護程序配置對話框的窗口類,如果使用標准的對話框,可以簡單地返回 TRUE。
在 ScreenSaverProc 窗口過程中,有個專用消息 WM_ERASEBKGND -- 可以在這時擦除背景,如果把這個消息傳到 DefScreenSaverProc,會得到一個全黑的背景。
使用時必須將編譯完成的 .exe 文件改名為 .scr 文件,然後拷貝到 Windows 或 Windows\System 目錄下。
為使控制面板能夠識別,屏幕保護程序的圖標(ICON)在資源文件中必須定義為 100,資源文件中必須包含一描述字符串。該字符串用於控制面板顯示屏幕保護程序的名字。它必須位於字符串表的首位,ID 為 100。資源文件中屏幕保護程序配置對話框的 ID 必須為2003。
綜上所述,屏幕保護程序的兩個部分即窗口過程和設置對話框過程是在不同的地方被執行的,所以在編程中要注意不要在兩個過程中傳遞變量,這就意味著屏幕保護程序的設置要被輸出到 .ini 或注冊表中保存,在窗口過程中的 WM_CREATE 消息中再讀入,因為你無法把它保留在內存中。同樣窗口過程和設置對話框過程中的變量或資源的初始化也要分別進行,比如說兩者都要用到同一個圖片,那就在自己的 WM_CREATE 消息中分別 LoadBitmap,總之要時刻認為你是在編寫兩個“獨立的程序”。
下面是一個屏幕保護程序的資源定義例子:
#include <resource.h>
#define ICO_MAIN 100
#define DLG_SETUP 2003
ICO_MAIN ICON "Resource\Main.ico"
DLG_SETUP DIALOG DISCARDABLE 0, 0, 300, 120
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "屏幕保護程序"
FONT 9, "宋體"
BEGIN
...
...
DEFPUSHBUTTON "確定(&O)", IDOK, 240,10,50,14
PUSHBUTTON "取消(&C)", IDCANCEL, 240,29,50,14
END
STRINGTABLE DISCARDABLE
BEGIN
100 "保護程序"
END
.def 文件例子:
EXPORTS
ScreenSaverProc
ScreenSaverConfigureDialog
RegisterDialogClasses
源代碼例子:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Programmed by 羅雲彬, [email protected]
; Website: http://asm.yeah.net
; LuoYunBin's Win32 ASM page (羅雲彬的編程樂園)
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 版本信息
; 屏幕保護程序模板
; Ver 1.0 - 2000.07.15
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.386
.model flat, stdcall
option casemap :none ; case sensitive
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 數據
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include user32.inc
include kernel32.inc
include comctl32.inc
include comdlg32.inc
include gdi32.inc
include advapi32.inc
include shell32.inc
include scrnsave.inc
includelib user32.lib
includelib kernel32.lib
includelib comctl32.lib
includelib comdlg32.lib
includelib gdi32.lib
includelib advapi32.lib
includelib shell32.lib
includelib scrnsave.lib
includelib msvcrt.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Equ 數據
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN equ 100 ;Must be 100
DLG_SETUP equ 2003 ;Must be 2003
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 數據段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?
hInstance dd ?
hWinMain dd ?
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代碼段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 設置對話框過程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ScreenSaverConfigureDialog proc uses ebx edi esi, \
hWnd:DWORD,wMsg:DWORD,wParam:DWORD,lParam:DWORD
mov eax,wMsg
;********************************************************************
.elseif eax == WM_CLOSE
invoke EndDialog,hWnd,NULL
;********************************************************************
.elseif eax == WM_COMMAND
mov eax,wParam
.if eax == IDOK
... 在此保存設置 ...
invoke EndDialog,hWnd,NULL
.elseif eax == IDCANCEL
invoke EndDialog,hWnd,NULL
.endif
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
ScreenSaverConfigureDialog endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 主程序窗口過程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ScreenSaverProc proc uses ebx edi esi, \
hWnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
mov eax,uMsg
.if eax == WM_CREATE
invoke GetModuleHandle,NULL
mov hInstance,eax
mov eax,hWnd
mov hWinMain,eax ...
在此初始化,包括定義一個定時器 ...
invoke SetTimer,hWinMain,TIMER_MOON,100,NULL
.elseif eax == WM_DESTROY
call _Quit
;********************************************************************
.elseif eax == WM_TIMER
...
在此畫屏幕動畫 ...
xor eax,eax
ret
;********************************************************************
; .elseif eax == WM_ERASEBKGND
;********************************************************************
; 以下黑屏的代碼在 DefScreenSaverProc 中已經包括,如果自己要處理
; 屏幕,可以把它去掉。
;********************************************************************
; invoke GetDC,hWnd
; mov @hDc,eax
; invoke GetClientRect,hWnd,addr @stRc
; invoke GetStockObject,BLACK_BRUSH
; invoke FillRect,@hDc,addr @stRc,eax
; invoke ReleaseDC,hWnd,@hDc
; xor eax,eax
; ret
.endif
;********************************************************************
invoke DefScreenSaverProc,hWnd,uMsg,wParam,lParam
ret
ScreenSaverProc endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 注冊設置對話框窗口Class過程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
RegisterDialogClasses proc uses ebx edi esi, hInst:DWORD
mov eax,TRUE
ret
RegisterDialogClasses endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end WinMain
以上是一個屏幕保護程序的框架結構,其實編寫一個屏幕保護程序的大部分工作量在於 WM_TIMER 消息的處理,也就是說對動畫的處理,如果就把上面的程序編譯,那麼你就會得到一個最簡單的“黑屏”屏保。這裡是我編寫的一個屏保月下情人,是用 Win32Asm 編寫的。