一、需求分析
由于sysInfo扫描的内容比较多,打算为它增加一点动画效果,提醒用户程序正在运行,耐心等待。
二、构建测试窗口
测试窗口上放置有一个按钮,按钮上的初始文字是“开始扫描”;并使用状态栏,状态栏初始状态不显示文字。
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ; 文 件 名:marquee.asm (GUI程序) ; 功 能: 文字动画 ; 开发环境:Win10 PRO + MASM32 v22 ; 作 者:PurpleEndurer,广西河池 ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< .586 .MODEL FLAT,STDCALL OPTION CASEMAP:NONE INCLUDE \masm32\include\windows.inc INCLUDE \masm32\include\kernel32.inc INCLUDELIB \masm32\lib\kernel32.lib INCLUDE \masm32\include\user32.inc INCLUDELIB \masm32\lib\user32.lib include \MASM32\INCLUDE\shell32.inc includelib \MASM32\LIB\shell32.lib include \masm32\include\comctl32.inc includelib \masm32\lib\comctl32.lib ;ssssssssssssssssssssssss ;.const ;ssssssssssssssssssssssss c_MainWinStyle equ NULL c_statusBarStyle equ SBARS_SIZEGRIP or WS_CHILD or WS_VISIBLE c_BtnStyle equ WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON c_BtnID equ 198 c_statusBarID equ 199 c_TimerID equ 1000 ;ssssssssssssssssssssssss ;PROTO ;ssssssssssssssssssssssss WinMain proto :HINSTANCE, :HINSTANCE, :LPSTR, :DWORD WndProc proto :HWND, :UINT, :WPARAM, :LPARAM ResizeMainWnd proto Marquee proto statusBarMarquee proto btnMarquee proto ;ssssssssssssssssssssssss .DATA ;ssssssssssssssssssssssss g_szClassName label byte g_szAppInfo db "文字动画 作者:PurpleEndurer, 广西河池", 0 g_szBtnClsName db "button", 0 g_szScan_btn db "开始扫描", 0 ;_btn 用于按钮 g_szStop_btn db "点击停止", 0 g_szLoopTxt_btn db "点击停止", 0 g_szScanning_Sb db "—正在扫描……", 0; _Sb用于状态栏 g_szScanOver_Sb db "扫描完成", 0 ; g_dwLoop_Sb dword 0 g_hInstance HANDLE ? g_hwndMain HANDLE ? g_hStatusBar HANDLE ? g_hBtn HANDLE ? ;ssssssssssssssssssssssss .CODE ;ssssssssssssssssssssssss start: invoke GetModuleHandle, NULL mov g_hInstance, eax invoke WinMain, g_hInstance, NULL, NULL, SW_SHOWDEFAULT invoke ExitProcess, eax invoke InitCommonControls ;====================================================== WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD ;====================================================== LOCAL wc:WNDCLASSEX LOCAL msg:MSG mov wc.cbSize,SIZEOF WNDCLASSEX mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra, NULL mov wc.cbWndExtra, NULL push hInst pop wc.hInstance mov wc.hbrBackground, COLOR_WINDOW+1 mov wc.lpszMenuName, NULL ;OFFSET MenuName mov wc.lpszClassName, OFFSET g_szClassName invoke LoadIcon,NULL,IDI_APPLICATION ;invoke LoadIcon, hInst, IDI_ICON mov wc.hIcon, eax mov wc.hIconSm, eax invoke LoadCursor, NULL, IDC_ARROW mov wc.hCursor, eax invoke RegisterClassEx, addr wc INVOKE CreateWindowEx, c_MainWinStyle, ADDR g_szClassName,ADDR g_szAppInfo,\ WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,500,400,NULL,NULL, hInst,NULL mov g_hwndMain, eax INVOKE ShowWindow, g_hwndMain, SW_SHOWNORMAL INVOKE UpdateWindow, g_hwndMain .WHILE TRUE INVOKE GetMessage, ADDR msg,NULL,0,0 .BREAK .IF (!eax) INVOKE TranslateMessage, ADDR msg INVOKE DispatchMessage, ADDR msg .ENDW mov eax, msg.wParam ret WinMain endp ;====================================================== WndProc proc uses ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM ;====================================================== mov eax, uMsg .IF eax==WM_CREATE ;创建状态栏 invoke CreateStatusWindow, c_statusBarStyle, NULL, hWnd, c_statusBarID mov g_hStatusBar, eax ;创建按钮 invoke CreateWindowEx, NULL, ADDR g_szBtnClsName, ADDR g_szScan_btn,\ c_BtnStyle, 75,70,140,25, hWnd, c_BtnID, g_hInstance, NULL mov g_hBtn, eax .ELSEIF eax==WM_COMMAND mov eax,wParam .if ax==c_BtnID shr eax,16 .IF ax==BN_CLICKED ;用户点击按钮 .if g_dwLoop_Sb==0 ;进行扫描状态 invoke SetTimer, hWnd, c_TimerID, 1000, NULL mov eax, OFFSET g_szStop_btn .else ;退出扫描状态 invoke KillTimer, hWnd, c_TimerID mov g_dwLoop_Sb, 0 invoke SendMessage, g_hStatusBar, SB_SETTEXT, 0, OFFSET g_szScanOver_Sb invoke RedrawWindow, g_hStatusBar, NULL, NULL, RDW_INTERNALPAINT mov eax, OFFSET g_szScan_btn .endif invoke SendMessage, g_hBtn ,WM_SETTEXT, 0, eax .ENDIF .endif .ELSEIF eax==WM_TIMER ;定时调用Marquee函数 invoke Marquee .ELSEIF eax==WM_SIZE invoke ResizeMainWnd .ELSEIF eax==WM_DESTROY invoke PostQuitMessage,NULL .ELSE invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret .ENDIF xor eax,eax ret WndProc endp ;====================================================== ResizeMainWnd proc ;====================================================== invoke MoveWindow, g_hStatusBar, 0, 0, 0, 0, TRUE ret ResizeMainWnd endp END start
三、在状态栏显示字符动画
记得以前DOS时代使用冠群金辰的杀毒软件kill,在扫描时会显示一个扫描形状的字符动画,当时觉得很有意思,映像深刻,这次就把这种效果用在状态栏上。
实现的方法就是当用户点扫描使用定时器定时刷新状态栏的文字。其中的扫描动态效果就是将依次状态栏文字的首字依次更新为—(编码:0AAA1h)、\(编码:0DCA3h)、|(编码:0FCA3h)、/(编码:0AFA3h)。
代码如下:
;====================================================== Marquee proc ;====================================================== invoke statusBarMarquee ret Marquee endp ;====================================================== statusBarMarquee proc uses eax ;====================================================== inc g_dwLoop_Sb .if g_dwLoop_Sb > 4 mov g_dwLoop_Sb, 1 .endif mov eax, offset g_szScanning_Sb .if g_dwLoop_Sb==1 mov word ptr [eax], 0DCA3h;'\' .elseif g_dwLoop_Sb==2 mov word ptr [eax], 0FCA3h;'|' .elseif g_dwLoop_Sb==3 mov word ptr [eax], 0AFA3h;'/' .elseif g_dwLoop_Sb==4 mov word ptr [eax], 0AAA1h;'—' .endif invoke SendMessage, g_hStatusBar, SB_SETTEXT, 0, OFFSET g_szScanning_Sb invoke RedrawWindow, g_hStatusBar, NULL, NULL, RDW_INTERNALPAINT ret statusBarMarquee endp
四、按钮跑马灯
窗口上的按钮初始文字是“开始扫描”,当我们点击按钮进入扫描状态时,我们将按钮文字改成“点击停止”并跑马灯。原理也是使用定时器,定期更新按钮上的文字。
代码如下:
;====================================================== Marquee proc ;====================================================== invoke btnMarquee invoke statusBarMarquee ret Marquee endp ;====================================================== btnMarquee proc uses eax ;ebx ;====================================================== mov eax, offset g_szLoopTxt_btn ;保存按钮文本首字 push word ptr [eax] ;mov bx, word ptr [eax] inc eax inc eax ; 按钮文字前移 .while byte ptr [eax]!=0 push word ptr [eax] pop word ptr [eax-2] inc eax inc eax .endw ;把按钮文本首字放到末尾 pop word ptr [eax-2] ;mov word ptr [eax-2], bx invoke SendMessage, g_hBtn ,WM_SETTEXT, 0, OFFSET g_szLoopTxt_btn invoke RedrawWindow, g_hBtn, NULL, NULL, RDW_INTERNALPAINT ret btnMarquee endp
五、程序运行效果
如下:
六、改进思路
文字动画在DOS的CGI时代还是颇具吸引力的,放到GUI时代复古一下也是一种怀念。
如果我们追求GUI的效果,可以对状态栏进行子类化,为它创建一个进度条。对按钮子类化,显示gif图片。
七、附记
进入Windows时代,Kill就像UCDOS一样淡出视野了。刚才为了确认无误,bing查了一下,冠群金辰的网站还在,只是内容很久没更新了。
不经意间顺带看到一篇关于火绒的文章,才知道火绒的几位创始人都出自瑞星……