用WIN汇编开发桌面报时工具
用WIN汇编开发桌面报时工具
用WIN32汇编语言开发出来的WINDOWS程序具有执行效率高、占用空间小等特点。
一、 软件简介及相应开发工具
桌面报时工具主要完成以下两个功能:
1、 F12热键语音报时、语音整点报时。
2、 在屏幕中上方直接显示时间,并不被任何窗口挡住。
3、 程序运行后最小化为任务栏图标。
笔者以WIN汇编为开发语言,以MASM32为开发工具,因为它不但可以免费获取和使用,而且自带IDE编程环境,读者可访问其官方网站下载MASM32开发包,官方网站为:http://www.movsd.com/。
二、设计思路
1、软件采用标准的自定义对话框架构实现。
2、采用微软的TTS语音引擎实现语音报时。 TTS基于COM,因此开发使用Microsoft SpeechSDK,直接调用SAPI实现语音朗读。
3、使用HOOK技术,截获F12按键进行语音报时。因此,笔者单独编写了一个DLL文件,捕捉F12按键。
由此可见,该报时工具虽小,但五脏俱全,涉及对话框消息循环、HOOK技术、COM接口、DLL文件编写、主要API的调用等技术。
三、实现代码
1、主程序代码
.586
.model flat, stdcall
option casemap :none
;#########################################################################
;首先声明头文件
include windows.inc
include user32.inc
include kernel32.inc
include gdi32.inc
include masm32.inc
include shell32.inc
include ole32.inc
include d:\masm32\com\include\oaidl.inc
include timersdll.inc
includelib user32.lib
includelib kernel32.lib
includelibgdi32.lib
includelib masm32.lib
includelib shell32.lib
includelib ole32.lib
includelib timersdll.lib
;#########################################################################
;声明宏(主要是控件ID和菜单ID)
IDC_ALARM equ 101
IDC_CANCEL equ 102
IDC_OK equ 103
IDC_ABOUT equ 104
IDC_CHECKBS equ 105
IDC_CHECKSHOWTIMER equ 106
IDR_MAINFRAME equ 107
IDR_MAINMENU equ 108
MENUID_SYSTEMSET equ 109
MENUID_SPEAKTIMER equ 110
MENUID_ABOUT equ 111
MENUID_EXIT equ 112
DIALOG_MAIN equ 1
IDT_TIMER equ 1
WM_NOTIFYICONN equ WM_USER+0
WM_CLOCKALARM equWM_USER+100h
;#########################################################################
;Microsoft Speech SDK没有相应的适应于汇编语言的INC头文件,笔者在代码
;文件中直接声明TTS相关虚函数。
;定义ISpVoice的虚函数接口表(根据SDK中的sapi.h中ISpVoiceVtbl的C++定义改
;成MASM32定义)
ISpVoice STRUCT DWORD
QueryInterface comethod3 ?
AddRef comethod1 ?
Release comethod1 ?
SetNotifySink comethod2 ?
SetNotifyWindowMessage comethod5 ?
SetNotifyCallbackFunction comethod4 ?
SetNotifyCallbackInterface comethod4 ?
SetNotifyWin32Event comethod1 ?
WaitForNotifyEvent comethod1 ?
GetNotifyEventHandle comethod1 ?
SetInterest comethod3 ?
GetEvents comethod4 ?
GetInfo comethod2 ?
SetOutput comethod3 ?
GetOutputObjectToken comethod2 ?
GetOutputStream comethod2 ?
Pause comethod1 ?
Resume comethod1 ?
SetVoice comethod2 ?
GetVoice comethod2 ?
Speak comethod4 ?
SpeakStream comethod4 ?
GetStatus comethod3 ?
Skip comethod4 ?
SetPriority comethod2 ?
GetPriority comethod2 ?
SetAlertBoundary comethod2 ?
GetAlertBoundary comethod2 ?
SetRate comethod2 ?
GetRate comethod2 ?
SetVolume comethod2 ?
GetVolume comethod2 ?
WaitUntilDone comethod2 ?
SetSyncSpeakTimeout comethod2 ?
GetSyncSpeakTimeout comethod2 ?
SpeakCompleteEvent comethod1 ?
IsUISupported comethod5 ?
DisplayUI comethod6 ?
ISpVoice ENDS
;#########################################################################
;可变数据段
.data?
szbufferprev db 20 dup(?)
szbuffernow db 20 dup(?)
hWinMain DWORD ?
idTIMER DWORD ?
CommandLine DWORD ?
hInstance DWORD ?
hicon HICON ?
hmenu DWORD ?
hlib DWORD ?
timershowx DWORD ?
timershowy DWORD ?
showrect RECT <>
;常量数据段
.data
szClassName db "Timers_Class",0
szdateformat db"yyyy年MM月dd日",0
sztimeformat db "hh点mm分ss秒",0
stnidstatus NOTIFYICONDATA <>
;======
isintpointalarmDWORD 1;0-表示整点不报时,1-整点报时
isshowtimer DWORD 1;0-表示不显示,1-显示
isintpoint DWORD 0;0-表示不是整点,1-是整点
;======
sztooltip db "桌面报时工具",0
szabout db "桌面报时工具由WIN32汇编开发",0
szmscap db "错误",0
szmstext1 db "无法在桌面上显示!",0
szmstext2 db "无法得到全屏DC!",0
;======
szprevdate db 50 dup(?)
szprevtime db 50 dup(?)
sznowdate db 50 dup(?)
sznowtime db 50 dup(?)
szbegin db "桌面报时工具为您报时"
sztext db 100 dup(?)
;#########################################################################
;代码段
.code
_showtext proto :DWORD
_ProcDlgMain proto :DWORD,:DWORD,:DWORD,:DWORD
_geterrno proto :DWORD,:DWORD
_speaktext proto :DWORD
_WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
_statusicon proto :DWORD
_configload proto :DWORD
start:
;程序的入口
invoke GetModuleHandle, NULL
mov hInstance, eax
invoke GetCommandLine
mov CommandLine, eax
invoke _WinMain,hInstance,NULL,CommandLine,SW_SHOWDEFAULT
invoke ExitProcess,eax
;#########################################################################
;得到当前时间子函数
_getnow proc
invoke lstrcpy,addr szprevtime,addr sznowtime
invoke lstrcpy,addr szprevdate,addr sznowdate
invoke GetDateFormat,NULL,NULL,NULL,addr szdateformat,addrsznowdate,50
invokeGetTimeFormat,LOCALE_USER_DEFAULT,TIME_FORCE24HOURFORMAT,NULL,addrsztimeformat,addr sznowtime,50
invoke lstrcpy,addr sztext,addr sznowdate
invokelstrcat,addr sztext,addr sznowtime
invokeszLeft,addr szprevtime,addr szbufferprev,2
invokeszLeft,addr sznowtime,addr szbuffernow,2
invokelstrcmp,addr szbufferprev,addr szbuffernow
testeax,eax
je @f
mov isintpoint,1
@@:
ret
_getnow endp
;#########################################################################
;显示时间子函数
;参数说明:lpsztex:要显示的文本的起始地址
_showtext proc uses ebxlpsztext:DWORD
LOCAL@Desktopdc:HDC
LOCAL@dwtextcolor,@dwbkcolor
mov@dwtextcolor,00010000h
mov@dwbkcolor,0000FF00h
invokeGetWindowDC,NULL
cmpeax,0
jne@f
invokeMessageBox,NULL,offset szmstext2,offset szmscap,MB_ICONERROR
@@:
mov@Desktopdc,eax
invokelstrlen,lpsztext
movebx,eax
invokeSetBkColor,@Desktopdc,@dwbkcolor
invokeSetTextColor,@Desktopdc,@dwtextcolor
invokeTextOut,@Desktopdc,timershowx,timershowy,lpsztext,ebx
cmpeax,0
jne@f
invokeMessageBox,NULL,offset szmstext1,offset szmscap,MB_ICONERROR
@@:
invokeReleaseDC,NULL,HDC
ret
_showtext endp
;#########################################################################
_showtime proc
invoke _showtext,offset sztext
ret
_showtime endp
;#########################################################################
;语音朗读文本子函数
;参数说明: pszspeakansi:要朗读的文本起始地址
_speaktext proc uses edx ebx pszspeakansi:DWORD
.data
szspeaktext db 200dup(?),0
szerrtext db "错误号:"
szerrtextno db 20 dup(?),0
ppVoice DWORD ?
CLSID_SpVoice GUID<96749377H,3391H,11D2H,<9EH,0E3H,00H,0C0H,4FH,79H,73H,96H>>
IID_ISpVoice GUID<6C44DF74H,72B9H,4992H,<0A1H,0ECH,0EFH,99H,6EH,04H,22H,0D4H>>
.code
invoke lstrlen,pszspeakansi
mov ebx,eax
invokeMultiByteToWideChar,CP_ACP,MB_PRECOMPOSED,pszspeakansi,-1,addrszspeaktext,ebx
invoke CoInitialize,NULL
;#######################################################################
;使用pVoice接口
invoke CoCreateInstance,addr CLSID_SpVoice,NULL,CLSCTX_ALL,addrIID_ISpVoice,addr ppVoice
.IF_FAILED
invoke _geterrno,eax,addr szerrtextno
invoke MessageBox,NULL,addr szerrtext,addr szmscap,MB_ICONERROR
jmp@f
.endif
mov eax,ppVoice
mov edx,[eax]
invoke (ISpVoice PTR[edx]).SetVoice,ppVoice,NULL
.IF_SUCCEEDED
mov eax,ppVoice
mov edx,[eax]
invoke(ISpVoice PTR[edx]).SetRate,ppVoice,3
.IF_FAILED
invoke _geterrno,eax,addr szerrtextno
invoke MessageBox,NULL,addr szerrtext,addr szmscap,MB_ICONERROR
.endif
mov eax,ppVoice
mov edx,[eax]
invoke(ISpVoice PTR[edx]).Speak,ppVoice,addr szspeaktext,0,NULL
.IF_FAILED
invoke _geterrno,eax,addr szerrtextno
invoke MessageBox,NULL,addr szerrtext,addr szmscap,MB_ICONERROR
.endif
.endif
mov eax,ppVoice
mov edx,[eax]
invoke (ISpVoice PTR[edx]).Release,ppVoice
@@:
call CoUninitialize
ret
_speaktext endp
;#########################################################################
;对话框消息处理函数
_ProcMain proc uses ebx hWnd,uMsg,wParam,lParam
LOCAL @stpos:POINT
.if uMsg==WM_TIMER
mov eax,wParam
.if eax==IDT_TIMER
call _getnow
.if isshowtimer==1
call_showtime
.endif
.if isintpointalarm==1 &&isintpoint==1
invoke _speaktext,addr szbegin
mov isintpoint,0
.endif
.endif
.elseif uMsg==WM_NOTIFYICONN
moveax,wParam
.if eax== IDR_MAINFRAME
mov eax,lParam
movzx eax,ax
.if eax== WM_RBUTTONUP
invoke GetCursorPos,addr @stpos
invokeTrackPopupMenu,hmenu,TPM_LEFTALIGN,@stpos.x,@stpos.y,NULL,hWnd,NULL
.endif
.endif
.elseif uMsg==WM_CLOCKALARM
invoke_speaktext,addr szbegin
.elseif uMsg==WM_COMMAND
moveax,wParam
movzxeax,ax
.ifeax==MENUID_EXIT
invoke InvalidateRect,NULL,NULL,NULL
invoke UnInstallHook,hWnd
invoke KillTimer,hWnd,idTIMER
invoke _statusicon,NIM_DELETE
invoke DestroyWindow,hWnd
.elseif eax==MENUID_SYSTEMSET
invoke _configload,0
invoke ShowWindow,hWinMain,SW_SHOWNORMAL
.elseif eax==MENUID_ABOUT
invokeMessageBox,NULL,addr szabout,addr sztooltip,NULL
.elseif eax==MENUID_SPEAKTIMER
invoke_speaktext,addr szbegin
.elseifeax==IDC_CANCEL
invokeShowWindow,hWinMain,SW_HIDE
.elseif eax==IDC_OK
call _configsave
invokeShowWindow,hWinMain,SW_HIDE
.endif
.elseif uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.else
invoke DefDlgProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
_ProcMain endp
;######################################################################### ;取得COM接口错误号函数,可以根据错误号在winerror.h中查到错误类型
_geterrno proc szerrhex:DWORD,pszerrtext:DWORD
push eax
invokedw2hex,szerrhex,pszerrtext
pop eax
ret
_geterrno endp
;######################################################################### ; 状态栏图标操作
_statusicon proc operation:DWORD
invoke Shell_NotifyIcon,operation,addr stnidstatus
ret
_statusiconendp
;######################################################################### ;主窗口消息循环
_WinMain proc hInst:DWORD,hPrevInst:DWORD,CmdLine:DWORD,CmdShow:DWORD
LOCAL @stwc:WNDCLASSEX
LOCAL @stmsg:MSG
LOCAL @CommandLine:DWORD
mov@stwc.cbSize, sizeof WNDCLASSEX
mov@stwc.style, CS_HREDRAW or CS_VREDRAW
mov@stwc.lpfnWndProc, offset _ProcMain
mov@stwc.cbClsExtra, NULL
mov@stwc.cbWndExtra, DLGWINDOWEXTRA
push hInst
pop @stwc.hInstance
mov @stwc.hbrBackground, COLOR_BTNFACE+1
mov @stwc.lpszMenuName, NULL
mov @stwc.lpszClassName, offset szClassName
invoke LoadIcon,hInst, IDR_MAINFRAME
mov hicon,eax
push hicon
pop @stwc.hIcon
push hicon
pop @stwc.hIconSm
invoke LoadCursor,NULL,IDC_ARROW
mov @stwc.hCursor,eax
invoke RegisterClassEx,addr @stwc
invoke CreateDialogParam,hInst,DIALOG_MAIN,NULL,NULL,NULL
mov hWinMain,eax
invoke LoadMenu,hInst,IDR_MAINMENU
mov hmenu,eax
invoke GetSubMenu,hmenu,0
mov hmenu,eax
mov stnidstatus.cbSize,sizeof NOTIFYICONDATA
push hWinMain
pop stnidstatus.hwnd
mov stnidstatus.uID,IDR_MAINFRAME
push hicon
pop stnidstatus.hIcon
mov stnidstatus.uFlags,NIF_ICON or NIF_TIP or NIF_MESSAGE
mov stnidstatus.uCallbackMessage,WM_NOTIFYICONN
invoke lstrcpy,addr stnidstatus.szTip,addr sztooltip
invoke ShowWindow,hWinMain,SW_HIDE
invoke UpdateWindow,hWinMain
invoke _configload,1
.while TRUE
invoke GetMessage,addr @stmsg,NULL,0,0
.BREAK .IF eax==0
invokeIsDialogMessage,hWinMain,addr @stmsg
.ifeax==FALSE
invoke TranslateMessage,addr @stmsg
invoke DispatchMessage,addr @stmsg
.endif
.endw
moveax,@stmsg.wParam
ret
_WinMain endp
;######################################################################### ;加载配置
_configload proc isfirstrun:DWORD
.code
.if isfirstrun==1 ;第一次运行,需要读取文件和初始化
;初始化
invoke GetDateFormat,NULL,NULL,NULL,addr szdateformat,addrsznowdate,50
invokeGetTimeFormat,LOCALE_USER_DEFAULT,TIME_FORCE24HOURFORMAT,NULL,addrsztimeformat,addr sznowtime,50
invoke SetTimer,hWinMain,IDT_TIMER,1000,NULL
mov idTIMER,eax
invoke _statusicon,NIM_ADD;创建状态栏图标
invokeInstallHook,hWinMain,WM_CLOCKALARM;安装热键钩子
invoke GetSystemMetrics,SM_CXSCREEN
shreax,1
movtimershowx, eax
movtimershowy, 1
.endif
.if isintpointalarm==0
invokeCheckDlgButton,hWinMain,IDC_CHECKBS,BST_UNCHECKED
.else
invoke CheckDlgButton,hWinMain,IDC_CHECKBS,BST_CHECKED
.endif
.if isshowtimer==0
invokeCheckDlgButton,hWinMain,IDC_CHECKSHOWTIMER,BST_UNCHECKED
.else
invoke CheckDlgButton,hWinMain,IDC_CHECKSHOWTIMER,BST_CHECKED
.endif
ret
_configload endp
;######################################################################### ;保存配置
_configsave proc
invoke IsDlgButtonChecked,hWinMain,IDC_CHECKBS
.if eax==BST_CHECKED
mov isintpointalarm,1
.else
mov isintpointalarm,0
.endif
invokeIsDlgButtonChecked,hWinMain,IDC_CHECKSHOWTIMER
.if eax==BST_CHECKED
mov isshowtimer,1
.else
mov isshowtimer,0
invoke InvalidateRect,NULL,NULL,NULL
.endif
ret
_configsave endp
;######################################################################### end start
2、 DLL文件代码
.586
.model flat, stdcall
option casemap:none
;#########################################################################
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib
.data?
hHook DWORD ?
hMainWnd DWORD ?
hWmessage DWORD ?
.data
hInstance DWORD 0
sztooltip db "桌面报时工具",0
;#########################################################################
.code
;#########################################################################
;DLL入口
DllEntry prochInst:HINSTANCE,reason:DWORD,userreserved:DWORD
push hInst
pop hInstance
mov eax,TRUE
ret
DllEntry endp
;#########################################################################
;键盘消息处理函数
KeyProc proc uses ebx dwCode:DWORD,wParam:DWORD,lParam:DWORD
invokeCallNextHookEx,hHook,dwCode,wParam,lParam
.if dwCode == HC_ACTION&&wParam==VK_F12
mov ebx,1
shl ebx,30
test lParam,ebx
jne @f
invoke SendMessage,hMainWnd,hWmessage,0,0
@@:
.endif
xor eax,eax
ret
KeyProc endp
;#########################################################################
;安装HOOK函数
InstallHook proc hwnd:DWORD,dwmessage:DWORD
push hwnd
pop hMainWnd
push dwmessage
pop hWmessage
invokeSetWindowsHookEx,WH_KEYBOARD,addr KeyProc,hInstance,NULL
mov hHook,eax
ret
InstallHook endp
;#########################################################################
;卸载HOOK函数
UnInstallHook proc hwnd:DWORD
invoke UnhookWindowsHookEx,hHook
ret
UnInstallHook endp
;#########################################################################
end DllEntry
3、 DLL导出文件内容(声明DLL的外部函数)
KeyProc proto :DWORD,:DWORD,:DWORD
InstallHook proto :DWORD,:DWORD
UnInstallHook proto :DWORD
上一篇: DOM 操作元素对象的属性和CSS样式