您的位置:首页 > 其它

Win32汇编---实现桌面时钟(有托盘图标)

2010-03-03 15:42 441 查看
忙了两三天的空余时间,终于把桌面时钟程序完成了,功能也是比较粗糙的。众所周知的,汇编写程序容易造成代码膨胀,本程序光代码就用了465行,对于我来说也算是不少了……

除了实现一些基本功能外,我还为程序实现了在托盘显示的功能。关于实现该功能,当然是费了不少时间,首先要感谢lczelion,他的理论支持下,让我知道了该如何去调用API,以及注意事项。主要还是windows外壳函数Shell_NotifyIcon和结构体NOTIFYICONDATA <>去配合实现的功能,具体如何调用网上也许有很多,就不去说了。但是还有一点就是如何实现窗口像QQ那样,只有托盘图标而把任务栏窗口隐藏?这是我所碰到的问题:原来CreateWindowEx中有个扩展属性WS_EX_TOOLWINDOW,该属性可以把我的想法实现。

对于最费事的还是如何去画时钟,去使用位图。如何使用DC以及ROP码去实现时钟图案和背景,如何去画时钟的指针和外围的圆点,如何去使用浮点寄存器去精确计算时钟指针的坐标来为我所用……这些都是我所碰到的问题以及学习到得知识。再此又要感谢老罗了~~

唯一一点没有按照我想法去走的就是如何只能让该实例程序只运行一个。由于时间关系,这个以后再去实现。据说要调用API,利用互斥法实现,具体思路和函数都知道了,实现也只是迟早的事情了。

程序中一些基本的语句就没有注释,关键的地方用注释表明了。不明白的地方亦可以把MSDN拿出来查查。下面就把代码贴上去:

资源文件Clock.rc的源代码如下:

//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
#include        <resource.h>
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
#define ICO_MAIN        100
#define IDC_MAIN        100
#define IDC_MOVE        101
#define IDB_BACK1       100
#define IDB_CIRCLE1     101
#define IDB_MASK1       102
#define IDB_BACK2       103
#define IDB_CIRCLE2     104
#define IDB_MASK2       105
#define	ICO_TRAYICON	106
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
ICO_MAIN        ICON    "Main.ico"
IDC_MAIN        CURSOR  "Main.cur"
IDC_MOVE        CURSOR  "Move.cur"
IDB_BACK1       BITMAP  "Back1.bmp"
IDB_CIRCLE1     BITMAP  "Circle1.bmp"
IDB_MASK1       BITMAP  "Mask1.bmp"
IDB_BACK2       BITMAP  "Back2.bmp"
IDB_CIRCLE2     BITMAP  "Circle2.bmp"
IDB_MASK2       BITMAP  "Mask2.bmp"
ICO_TRAYICON	ICON	"TrayIcon.ico"
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<


Clock.asm源代码:

;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
.386
.model  flat, stdcall
option  casemap: none
;----------------------------------------------------------------
include         windows.inc
include         user32.inc
includelib      user32.lib
include         kernel32.inc
includelib      kernel32.lib
include         gdi32.inc
includelib      gdi32.lib
include         shell32.inc
includelib      shell32.lib
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
WM_SHELLNOTIFY  equ     WM_USER + 1
IDI_TRAYICON    equ     1       ;托盘小图标 ID

CLOCK_SIZE      equ     150
ICO_MAIN        equ     100
IDC_MAIN        equ     100
IDC_MOVE        equ     101

IDB_BACK1       equ     100
IDB_CIRCLE1     equ     101
IDB_MASK1       equ     102
IDB_BACK2       equ     103
IDB_CIRCLE2     equ     104
IDB_MASK2       equ     105
ICO_TRAYICON    equ     106

ID_TIMER        equ     1
IDM_BACK1       equ     100
IDM_BACK2       equ     101
IDM_CIRCLE1     equ     102
IDM_CIRCLE2     equ     103
IDM_TOPMOST     equ     104
IDM_HIDE        equ     105
IDM_SHOW        equ     106
IDM_EXIT        equ     107
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
.data?

hInstance       dd      ?
hWinMain        dd      ?
hCursorMain     dd      ?
hCursorMove     dd      ?
hMenu           dd      ?

hBmpBack        dd      ?
hDcBack         dd      ?
hBmpClock       dd      ?
hDcClock        dd      ?

dwNowBack       dd      ?
dwNowCircle     dd      ?

stNote  NOTIFYICONDATA <>
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
.const

szClassName     db      "Clock",0
dwParam180      dw      180
dwRadius        dw      CLOCK_SIZE/2
szTrayTip       db      "Powered by Win32 Assembly",0
szMenuBack1     db      "使用格子背景(&A)",0
szMenuBack2     db      "使用花布背景(&B)",0
szMenuCircle1   db      "使用淡蓝色边框(&C)",0
szMenuCircle2   db      "使用粉红色边框(&D)",0
szMenuTopMost   db      "总在最前(&T)",0
szMenuHide      db      "隐藏时钟窗口(&H)",0
szMenuShow      db      "显示时钟窗口(&S)",0
szMenuExit      db      "退出(&X)...",0
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
.code
;========================================
;计算出圆周上某个角度对应的 X 坐标
; X = 圆心X + sin(角度)*半径
;========================================
_CalcX          proc    _dwDegree, _dwRadius
local   @dwReturn:DWORD

fild    dwRadius
fild    _dwDegree
fldpi
fmul
fild    dwParam180
fdivp   st(1),st
fsin
fild    _dwRadius
fmul
fadd
fistp   @dwReturn
mov     eax,@dwReturn
ret

_CalcX          endp
;========================================
;计算出圆周上某个角度对应的 Y 坐标
; Y = 圆心Y - cos(角度)*半径
;========================================
_CalcY          proc    _dwDegree, _dwRadius
local   @dwReturn:DWORD

fild    dwRadius
fild    _dwDegree
fldpi
fmul
fild    dwParam180
fdivp   st(1),st
fcos
fild    _dwRadius
fmul
fsubp   st(1),st
fistp   @dwReturn
mov     eax,@dwReturn
ret

_CalcY          endp
;========================================
; 画 _dwDegree 角度的线条,
;  半径 = _dwRadius
;========================================
_DrawLine       proc    _hDC, _dwDegree, _dwRadius
local   @dwX1, @dwY1
local   @dwX2, @dwY2

invoke  _CalcX,_dwDegree,_dwRadius
mov     @dwX1,eax
invoke  _CalcY,_dwDegree,_dwRadius
mov     @dwY1,eax
;---------------------------------
add     _dwDegree,180
invoke  _CalcX,_dwDegree,10
mov     @dwX2,eax
invoke  _CalcY,_dwDegree,10
mov     @dwY2,eax

invoke  MoveToEx,_hDC,@dwX1,@dwY1,NULL
invoke  LineTo,_hDC,@dwX2,@dwY2
ret

_DrawLine       endp
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
_CreateClockPic proc
local   @stTime:SYSTEMTIME

pushad
invoke  BitBlt,hDcClock,0,0,CLOCK_SIZE,CLOCK_SIZE,hDcBack,0,0,SRCCOPY

;=========================画时钟指针
invoke  GetLocalTime,addr @stTime
invoke  CreatePen,PS_SOLID,1,0
invoke  SelectObject,hDcClock,eax
invoke  DeleteObject,eax
movzx   eax,@stTime.wSecond
mov     ecx,360/60
mul     ecx
invoke  _DrawLine,hDcClock,eax,60
;-----------------------------------
invoke  CreatePen,PS_SOLID,2,0
invoke  SelectObject,hDcClock,eax
invoke  DeleteObject,eax
movzx   eax,@stTime.wMinute
mov     ecx,360/60
mul     ecx
invoke  _DrawLine,hDcClock,eax,56
;-----------------------------------
invoke  CreatePen,PS_SOLID,3,0
invoke  SelectObject,hDcClock,eax
invoke  DeleteObject,eax
movzx   eax,@stTime.wHour
.if     eax >=  12
sub     eax,12
.endif
mov     ecx,360/12
mul     ecx
movzx   ecx,@stTime.wMinute
shr     ecx,1
add     eax,ecx
invoke  _DrawLine,hDcClock,eax,53

invoke  GetStockObject,NULL_PEN
invoke  SelectObject,hDcClock,eax
invoke  DeleteObject,eax
popad
ret

_CreateClockPic endp
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
_CreateBackGround proc
local   @hDC, @hDcCircle, @hDcMask
local   @hBmpBack, @hBmpCircle, @hBmpMask

invoke  GetDC,hWinMain
mov     @hDC,eax
invoke  CreateCompatibleDC,@hDC
mov     hDcBack,eax
invoke  CreateCompatibleDC,@hDC
mov     hDcClock,eax
invoke  CreateCompatibleDC,@hDC
mov     @hDcCircle,eax
invoke  CreateCompatibleDC,@hDC
mov     @hDcMask,eax
invoke  CreateCompatibleBitmap,@hDC,CLOCK_SIZE,CLOCK_SIZE
mov     hBmpBack,eax
invoke  CreateCompatibleBitmap,@hDC,CLOCK_SIZE,CLOCK_SIZE
mov     hBmpClock,eax
invoke  ReleaseDC,hWinMain,@hDC

invoke  LoadBitmap,hInstance,dwNowBack
mov     @hBmpBack,eax
invoke  LoadBitmap,hInstance,dwNowCircle
mov     @hBmpCircle,eax
mov     eax,dwNowCircle
inc     eax
invoke  LoadBitmap,hInstance,eax
mov     @hBmpMask,eax

invoke  SelectObject,hDcBack,hBmpBack
invoke  SelectObject,hDcClock,hBmpClock
invoke  SelectObject,@hDcCircle,@hBmpCircle
invoke  SelectObject,@hDcMask,@hBmpMask

;==========================以背景图片填充
invoke  CreatePatternBrush,@hBmpBack
push    eax
invoke  SelectObject,hDcBack,eax
invoke  PatBlt,hDcBack,0,0,CLOCK_SIZE,CLOCK_SIZE,PATCOPY
pop     eax
invoke  DeleteObject,eax

;>>>>>>>>>>>关键技术<<<<<<<<<<<
;///////////////////////////
; 块传送函数中对于 ROP 码的操作
;///////////////////////////
;==========================画钟面
invoke  BitBlt,hDcBack,0,0,CLOCK_SIZE,CLOCK_SIZE,@hDcMask,0,0,SRCAND
invoke  BitBlt,hDcBack,0,0,CLOCK_SIZE,CLOCK_SIZE,@hDcCircle,0,0,SRCPAINT

invoke  DeleteDC,@hDcCircle
invoke  DeleteDC,@hDcMask
invoke  DeleteObject,@hBmpBack
invoke  DeleteObject,@hBmpCircle
invoke  DeleteObject,@hBmpMask
ret

_CreateBackGround endp
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
_Initial        proc
;========================初始化托盘图标
mov     stNote.cbSize,sizeof NOTIFYICONDATA
push    hWinMain
pop     stNote.hwnd
mov     stNote.uID,IDI_TRAYICON
mov     stNote.uFlags,NIF_ICON+NIF_MESSAGE+NIF_TIP
mov     stNote.uCallbackMessage,WM_SHELLNOTIFY
invoke  LoadIcon,hInstance,ICO_TRAYICON
mov     stNote.hIcon,eax
invoke  lstrcpy,addr stNote.szTip,addr szTrayTip
invoke  Shell_NotifyIcon,NIM_ADD,offset stNote

;========================创建弹出菜单项
invoke  CreatePopupMenu
mov     hMenu,eax

invoke  AppendMenu,hMenu,MF_BYCOMMAND,IDM_BACK1,offset szMenuBack1
invoke  AppendMenu,hMenu,MF_BYCOMMAND,IDM_BACK2,offset szMenuBack2
invoke  AppendMenu,hMenu,MF_SEPARATOR,0,0
invoke  AppendMenu,hMenu,MF_BYCOMMAND,IDM_CIRCLE1,offset szMenuCircle1
invoke  AppendMenu,hMenu,MF_BYCOMMAND,IDM_CIRCLE2,offset szMenuCircle2
invoke  AppendMenu,hMenu,MF_SEPARATOR,0,0
invoke  AppendMenu,hMenu,MF_BYCOMMAND,IDM_TOPMOST,offset szMenuTopMost
invoke  AppendMenu,hMenu,MF_BYCOMMAND,IDM_HIDE,offset szMenuHide
invoke  AppendMenu,hMenu,MF_BYCOMMAND,IDM_SHOW,offset szMenuShow
invoke  AppendMenu,hMenu,MF_SEPARATOR,0,0
invoke  AppendMenu,hMenu,MF_BYCOMMAND,IDM_EXIT,offset szMenuExit

invoke  CheckMenuRadioItem,hMenu,IDM_BACK1,IDM_BACK2,IDM_BACK1,MF_BYCOMMAND
invoke  CheckMenuRadioItem,hMenu,IDM_CIRCLE1,IDM_CIRCLE2,IDM_CIRCLE1,MF_BYCOMMAND

;========================设置圆形窗口
invoke  CreateEllipticRgn,0,0,CLOCK_SIZE+1,CLOCK_SIZE+1
push    eax
invoke  SetWindowRgn,hWinMain,eax,TRUE
pop     eax
invoke  DeleteObject,eax
;========================建立背景
mov     dwNowBack,IDB_BACK1
mov     dwNowCircle,IDB_CIRCLE1
invoke  _CreateBackGround
invoke  _CreateClockPic
;========================建立周期为 1s 的计时器
invoke  SetTimer,hWinMain,ID_TIMER,1000,NULL
ret

_Initial        endp
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
_DelBackGround  proc
invoke  DeleteDC,hDcBack
invoke  DeleteDC,hDcClock
invoke  DeleteObject,hBmpBack
invoke  DeleteObject,hBmpClock
ret

_DelBackGround  endp
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
_Quit           proc
invoke  _DelBackGround
invoke  KillTimer,hWinMain,ID_TIMER
invoke  Shell_NotifyIcon,NIM_DELETE,addr stNote
invoke  DestroyWindow,hWinMain
invoke  PostQuitMessage,NULL
invoke  DestroyMenu,hMenu
ret

_Quit           endp
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
_ProcWinMain    proc    uses ebx edi esi hWnd, uMsg, wParam, lParam
local   @stPS:PAINTSTRUCT
local   @hDC
local   @stPos:POINT

mov     eax,uMsg
.if     eax ==  WM_TIMER
invoke  _CreateClockPic
invoke  InvalidateRect,hWnd,NULL,FALSE
.elseif eax ==  WM_PAINT
invoke  BeginPaint,hWnd,addr @stPS
mov     @hDC,eax
mov     eax,@stPS.rcPaint.right
sub     eax,@stPS.rcPaint.left
mov     ecx,@stPS.rcPaint.bottom
sub     ecx,@stPS.rcPaint.top
invoke  BitBlt,@hDC,@stPS.rcPaint.left,@stPS.rcPaint.top,eax,ecx,/
hDcClock,@stPS.rcPaint.left,@stPS.rcPaint.top,SRCCOPY
invoke  EndPaint,hWnd,addr @stPS
.elseif eax ==  WM_RBUTTONDOWN
invoke  GetCursorPos,addr @stPos
invoke  EnableMenuItem,hMenu,IDM_SHOW,MF_GRAYED
invoke  TrackPopupMenu,hMenu,TPM_LEFTALIGN,@stPos.x,@stPos.y,NULL,hWnd,NULL
.elseif eax ==  WM_LBUTTONDOWN
invoke  SetCursor,hCursorMove
invoke  UpdateWindow,hWnd
invoke  ReleaseCapture
invoke  SendMessage,hWnd,WM_NCLBUTTONDOWN,HTCAPTION,0
invoke  SetCursor,hCursorMain
.elseif eax ==  WM_COMMAND
mov     eax,wParam
.if     ax == IDM_BACK1
mov     dwNowBack,IDB_BACK1
invoke  CheckMenuRadioItem,hMenu,IDM_BACK1,IDM_BACK2,IDM_BACK1,MF_BYCOMMAND
.elseif ax == IDM_BACK2
mov     dwNowBack,IDB_BACK2
invoke  CheckMenuRadioItem,hMenu,IDM_BACK1,IDM_BACK2,IDM_BACK2,MF_BYCOMMAND
.elseif ax == IDM_CIRCLE1
mov     dwNowCircle,IDB_CIRCLE1
invoke  CheckMenuRadioItem,hMenu,IDM_CIRCLE1,IDM_CIRCLE2,IDM_CIRCLE1,MF_BYCOMMAND
.elseif ax == IDM_CIRCLE2
mov     dwNowCircle,IDB_CIRCLE2
invoke  CheckMenuRadioItem,hMenu,IDM_CIRCLE1,IDM_CIRCLE2,IDM_CIRCLE2,MF_BYCOMMAND
.elseif ax == IDM_TOPMOST
movzx   eax,ax
mov     ebx,eax
invoke  GetMenuState,hMenu,ebx,MF_BYCOMMAND
.if     eax ==  MF_CHECKED
invoke  CheckMenuItem,hMenu,ebx,MF_UNCHECKED
invoke  SetWindowPos,hWnd,HWND_NOTOPMOST,0,0,0,0,SWP_NOMOVE or SWP_NOSIZE
.else
invoke  CheckMenuItem,hMenu,ebx,MF_CHECKED
invoke  SetWindowPos,hWnd,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE or SWP_NOSIZE
.endif
.elseif ax == IDM_SHOW
invoke	ShowWindow,hWnd,SW_RESTORE
;invoke  SetWindowPos,hWnd,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE or SWP_NOSIZE
;invoke  SetWindowPos,hWnd,HWND_NOTOPMOST,0,0,0,0,SWP_NOMOVE or SWP_NOSIZE
invoke	SetForegroundWindow,hWnd
.elseif ax == IDM_HIDE
invoke  ShowWindow,hWnd,SW_HIDE
.elseif ax == IDM_EXIT
call    _Quit
xor     eax,eax
ret
.endif
invoke  _DelBackGround
invoke  _CreateBackGround
invoke  _CreateClockPic
invoke  InvalidateRect,hWnd,NULL,FALSE
.elseif eax ==  WM_SHELLNOTIFY
.if     wParam == IDI_TRAYICON
.if     lParam == WM_RBUTTONDOWN
invoke  GetCursorPos,addr @stPos
invoke  SetForegroundWindow,hWnd
invoke  EnableMenuItem,hMenu,IDM_SHOW,MF_ENABLED
invoke  TrackPopupMenu,hMenu,TPM_RIGHTALIGN,@stPos.x,@stPos.y,NULL,hWnd,NULL
invoke  PostMessage,hWnd,WM_NULL,0,0
.elseif lParam == WM_LBUTTONDBLCLK
invoke  SendMessage,hWnd,WM_COMMAND,IDM_SHOW,0

.endif
.endif
.elseif eax ==  WM_CREATE
mov     eax,hWnd
mov     hWinMain,eax
invoke  _Initial
.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  LoadCursor,hInstance,IDC_MAIN
mov     hCursorMain,eax
invoke  LoadCursor,hInstance,IDC_MOVE
mov     hCursorMove,eax

invoke  RtlZeroMemory,addr @stWndClass,sizeof @stWndClass

invoke  LoadIcon,hInstance,ICO_MAIN
mov     @stWndClass.hIcon,eax
mov     @stWndClass.hIconSm,eax
push    hCursorMain
pop     @stWndClass.hCursor
push    hInstance
pop     @stWndClass.hInstance
mov     @stWndClass.cbSize,sizeof WNDCLASSEX
mov     @stWndClass.style,CS_HREDRAW or CS_VREDRAW or CS_DBLCLKS
mov     @stWndClass.lpfnWndProc,offset _ProcWinMain
mov     @stWndClass.hbrBackground,COLOR_WINDOW + 1
mov     @stWndClass.lpszClassName,offset szClassName

invoke  RegisterClassEx,addr @stWndClass
invoke  CreateWindowEx,WS_EX_TOOLWINDOW,/
offset szClassName,offset szClassName,/
WS_SYSMENU or WS_POPUP,/
900,100,CLOCK_SIZE,CLOCK_SIZE,/
NULL,NULL,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


实验图片如下:

时钟的图片:



鼠标放在托盘上:



在时钟上点击右键:



在托盘图标上点击右键:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: