用Win32汇编实现标题滚动效果
   作者:zklhp 于2009-8-16上传 

 

    偶用Win32汇编实现了一下标题滚动的效果,由于使用了Unicode,自己总结出了一些经验,发出来跟各位分享一下,也支持一下杂志。

    先把代码帖一下吧,因为这样看排版不太好,建议大家看打包的代码。

;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm

ID_Timer equ 1

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
WndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD


.DATA
szClassName db 04Dh, 000h, 041h, 000h, 053h, 000h, 04Dh, 000h, 050h, 000h, 06Ch, 000h, 075h, 000h
db 073h, 000h, 05Fh, 000h, 043h, 000h, 06Ch, 000h, 061h, 000h, 073h, 000h, 073h, 000h,0,0
;"MASMPlus_Class",0

szWindowName db 02Fh, 065h, 001h, 063h, 041h, 000h, 06Fh, 000h, 067h, 000h, 06Fh, 000h, 020h, 000h, 02Fh, 065h
db 001h, 063h, 04Dh, 000h, 061h, 000h, 073h, 000h, 06Dh, 000h, 070h, 000h, 06Ch, 000h, 075h, 000h
db 073h, 000h, 020h, 000h, 02Fh, 065h, 001h, 063h, 077h, 000h, 077h, 000h, 077h, 000h, 02Eh, 000h
db 061h, 000h, 06Fh, 000h, 067h, 000h, 06Fh, 000h, 073h, 000h, 06Fh, 000h, 066h, 000h, 074h, 000h
db 02Eh, 000h, 063h, 000h, 06Fh, 000h, 06Dh, 000h, 021h, 000h,0,0
;"支持Aogo 支持Masmplus 支持www.aogosoft.com!",0
.DATA?
hInstance dd ?
szBuf db 128 dup(?)
dwLen dd ? ;标题长度 字节数
dwNum dd ? ;走过的长度 字节数

.CODE
START:

invoke GetModuleHandle,NULL
mov hInstance,eax
invoke WinMain,hInstance,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0

WinMain proc hInst:DWORD,hPrevInst:DWORD,CmdLine:DWORD,CmdShow:DWORD
LOCAL wc :WNDCLASSEX
LOCAL msg :MSG
local hWnd :HWND

;因为使用了Unicode 所以与字符串有关的API都应该用Unicode版
mov wc.cbSize,sizeof WNDCLASSEX
mov wc.style,CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW
mov wc.lpfnWndProc,offset WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_BTNFACE+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,offset szClassName
invoke LoadIconW,hInst,100
mov wc.hIcon,eax
invoke LoadCursorW,NULL,IDC_ARROW
mov wc.hCursor,eax
mov wc.hIconSm,0
invoke RegisterClassExW, ADDR wc
invoke CreateWindowExW,NULL,offset szClassName,offset szWindowName,WS_OVERLAPPEDWINDOW,200,200,400,200,NULL,NULL,hInst,NULL
mov hWnd,eax
invoke ShowWindow,hWnd,SW_SHOWNORMAL
invoke UpdateWindow,hWnd

StartLoop:
invoke GetMessageW,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessageW, ADDR msg
jmp StartLoop
ExitLoop:

mov eax,msg.wParam
ret
WinMain endp

WndProc proc hWin:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
.if uMsg==WM_CREATE
invoke SetTimer,hWin,ID_Timer,300,0 ;定时器 0.3s触发一次
xor eax,eax
mov dwNum,eax
invoke lstrlenW,offset szWindowName ;得到的长度是字数 字节数应乘2
shl eax,1h
mov dwLen,eax
.elseif uMsg == WM_TIMER
mov eax,dwNum
inc eax
inc eax
mov dwNum,eax ;每次加2 因为是Unicode 不存在对齐的问题
lea eax,szWindowName
add eax,dwNum
invoke lstrcpyW,offset szBuf,eax
;先将后面的复制过去
invoke lstrlenW,offset szBuf
shl eax,1
lea ecx,szBuf
add ecx,eax ;指向刚才复制的字符串的末尾
sub eax,dwLen
neg eax ;求出剩下的长度
invoke lstrcpynW,ecx,offset szWindowName,eax
mov eax,dwNum
.if eax >= dwLen ;若当前位置大于长度则重新开始
xor eax,eax
mov dwNum,eax
.endif
invoke SetWindowTextW,hWin,offset szBuf
.elseif uMsg == WM_DESTROY
invoke PostQuitMessage,NULL
.else
invoke DefWindowProcW,hWin,uMsg,wParam,lParam
.endif
ret
WndProc endp

END START

    程序看起来长,其实就是定时器消息处理的部分比较重要

.elseif uMsg == WM_TIMER
mov eax,dwNum
inc eax
inc eax
mov dwNum,eax ;每次加2 因为是Unicode 不存在对齐的问题
lea eax,szWindowName
add eax,dwNum
invoke lstrcpyW,offset szBuf,eax
;先将后面的复制过去
invoke lstrlenW,offset szBuf
shl eax,1
lea ecx,szBuf
add ecx,eax ;指向刚才复制的字符串的末尾
sub eax,dwLen
neg eax ;求出剩下的长度
invoke lstrcpynW,ecx,offset szWindowName,eax
mov eax,dwNum
.if eax >= dwLen ;若当前位置大于长度则重新开始
xor eax,eax
mov dwNum,eax
.endif
invoke SetWindowTextW,hWin,offset szBuf
 
 

    算法比较简单,说白了就是要将标题分成两部分,利用dwNum控制当前的位置,将dwNum之后的字符复制到缓冲区,再将剩余的字符用从头开始的内容填充,若dwNum超过标题长度则从头开始,连续进行这一操作就可以看到滚动效果了。

    这里,我想说的一点是Unicode的使用。因为使用ASCII的话,中文是两个字节,但英语字母、数字、标点等内容都是一个字节,会对标题滚动效果的实现造成困难,因此我使用了Unicode,所有的内容都是两字节,我们只要执行加2就可以完美实现标题滚动。但这就要求我们的字符串是Unicode,比如文中给出的程序,字符串都是04Dh, 000h, 041h形式。因为我们汇编对Unicode支持不好,这一步只能手动完成,比如用记事本转化。(如果哪位高手有高招可得告诉俺啊)而且,使用的所有的字符串处理函数都应该是Unicode版,就是加W的API,比如lstrcpynW,lstrcpyW,lstrlenW等。这一步比较容易忽略的就是像DispatchMessageW、GetMessageW这样的函数,它们也应该是Unicode版,否则实现不了。再有就是一些API的工作特性,比如lstrlenW返回的是字数,也就是长度的一半,我们要用字节数得自己乘2等等。程序的运行效果如图所示。

 

    综上,我用最简单的方法实现了标题滚动的效果,初步探索了利用汇编写Unicode程序。总的来说程序比较简单,关键是一种尝试。希望大家能从我这个小程序程序中得到些许灵感,这就是写作本文的最大目的吧。

    本人汇编水平一般,文中不当之处还望各位高手指出。


 

运行结果




<<<上一篇
欢迎访问AoGo汇编小站:http://www.aogosoft.com
下一篇>>>