欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

用WIN汇编开发桌面报时工具

程序员文章站 2022-04-29 15:54:43
...

用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