分类目录归档:汇编&逆向

Windows 文件操作 — 创建和读写

1 打开和关闭文件
打开文件使用CreateFile 用法如下

invoke CreateFile,lpFileName,dwDesiredAccess,dwShareMode,lpSecurityAttributes,dwCreationDisposition,dwFlagsAndAttributes,hTemplateFile

各参数含义如下:
lpFileName:指向存放有文件名的缓冲区,文件名是以0结尾的字符串,字符最大长度为MAX_PATH(windows.inc中为260),可以打开多种对象,包括:控制台(Consoles),通信设备(Communications resources)-包括串口等,目录(Directories)和文件(Files),邮件槽(Mailslots),管道(Pipes)

dwDesiredAccess参数是存取方式,可以取的值有两个
GENERIC_READ 表示读取文件数据
GENERIC_WRITE 表示写数据
如果要读写数据,要同时指定这两个标志

dwShareMode参数是共享属性,表明文件被打开后是否允许其它进程再次打开文件, 可以取的值有四个
0:不允许文件再被打开
FILE_SHARE_DELETE:允许其它进程同时对文件进行删除
FILE_SHARE_READ:允许其它进程以读方式打开文件
FILE_SHARE_WRITE:允许其它进程同时以写方式打开文件

lpSecurityAttributes参数为安全属性,指定文件句柄是否可以被子进程继承,为NULL表明无法被继承,否则要将参数指向一个SECURITY_ATTRIBUTES结构,该结构定义如下

SECURITY_ATTRIBUTES STRUCT
nLength DWORD ? ;本结构的长度
lpSectrityDescriptor DWORD ?
bInheritHandle DWORD ? ;是否允许继承
SECURITY_ATTRIBUTES ENDS

将nLength设置为结构的长度,将bInheritHandle设置为TRUE就可以使句柄能够被子进程继承

dwCreationDisposition参数用来设置文件已经存在或不存在时系统采取的动作,在这里可以指定函数要执行的功能是创建还是打开.共有5个值:
CREATE_NEW:创建新文件,如果文件以存在函数会返回失败
CREATE_ALWAYS:创建新文件,如果文件以存在则清除原文件
OPEN_EXISTING:打开存在的文件,当文件不存在时函数返回失败
OPEN_ALWAYS:如果文件以存在,则打开,不存在则创建新文件
TRUNCATE_EXISTING:打开文件并将文件截断为零,当文件不存在时,返回失败

dwFlagsAndAttributes参数用来指定新建文件的属性,可以有10个值
FILE_ATTRIBUTE_NORMAL:普通文件,设置这个属性时其他属性都不会生效
FILE_ATTRIBUTE_ARCHIVE:设置归档属性
FILE_ATTRIBUTE_HIDDEN:设置隐藏属性
FILE_ATTRIBUTE_READONLY:设置只读属性
FILE_ATTRIBUTE_SYSTEM:设置系统属性
FILE_ATTRIBUTE_TEMPORARY:临时文件,系统会尽量把文件内容保持在内存中,以加快存取速度,程序在不使用它时会尽快删除它
此外还可以指定对文件操作的方式
FILE_FLAG_WRITE_THROUGH:使用WriteThrough模式,系统不会对文件使用缓存.
FILE_FLAG_OVERLAPPED:使用异步文件操作模式
FILE_FLAG_DELETE_ON_CLOSE:文件被关闭后立即被系统自动删除
FILE_FLAG_RANDOM_ACCESS:对文件进行随机读写操作

hTemplateFile:指定了一个文件模板的句柄,该文件模板的所有属性都会被复制到当前创新的文件中.win95不支持本参数,建议在参数中使用NULL.当打开或创建文件成功的时候函数返回一个文件句柄,失败返回INVALID_HANDLE_VALUE,这个值是-1而不是NULL

关闭文件用

invoke CloseHandle,hFile

2移动文件指针
调整文件指针使用SetFilePointer函数

invoke SetFilePointer,hFile,lDistanceToMove,lpDistanceToMoveHigh,dwMoveMethod

hFile:是CreateFile返回的文件句柄
lDistanceToMove:指定要移动的距离.
ldistanceToMoveHigh:是一个指针指向一个32位变量存放移动距离的高32位,他与lDistanceToMove组成一个64位地址,但在X86平台上的windows版本中,文件长度不会超过4G,所以一般设置为NULL
dwMoveMethod指定了移动模式,有3个值
FILE_GEGIN:总是从文件头部开始移动
FILE_CURRENT:从当前的文件指针处开始移动
FILE_END:从文件尾开始移动
当lDistanceToMoveHigh=NULL时,如果函数执行失败返回值是-1,否则返回新的文件指针

可以使用以下代码获得当前指针的位置

invoke SetFilePointer,hFile,0,NULL,FILE_CURRENT

SetEndOfFile设置文件长度

invoke SetEndOfFile,hFile

3读写文件
读写文件可以使用ReadFile/WriteFile,这两个函数读写方式可以是同步或异步
也可以用ReadFileEx/WriteFileEx,这两个函数只用于异步读写

ReadFile用法

invoke ReadFile,hFile,lpBuffer,nNumberOfBytesToRead,lpNumberOfBytesRead,lpOverlapped

hFile是文件句柄
lpBuffer指向一个缓冲区,函数会将读出的数据传送到这里
nNumberOfBytesToRead指定需要读入的字节数
lpNumberOfBytesRead指向一个dword变量,函数将在这里返回实际读入的字节数
lpOverlapped参数指向一个OVERLAPPED结构,供函数在异步读取文件时使用,同步读写中为NULL
读取文件失败,则返回0,成功则函数返回非0值
例:

invoke ReadFile,@hFile,addr @szReadBuffer,sizeof @szReadBuffer,addr @dwBytesRead,0

读出的字节数保存在@dwBytesRead中.

WriteFile用法

invoke WriteFile,hFile,lpBuffer,nNumberOfBytesToWrite,lpNumberOfBytesWriten,lpOverlapped

hFile:文件句柄
lpBuffer:指向一个缓冲区,当中包含有要写入的文件数据
nNumberOfBytesToWrite:指定要写入的字节数
lpNumberOfBytesWriten:函数在这里返回成功写入的字节数
lpOverlapped:异步结构,同步时为NULL
写入文件时可能会由于多种原因使数据丢失
可以使用FlushFileBuffers函数来清空数据缓冲区

invoke FlushFileBuffers,hFile

总之如果这个函数执行成功的话,就能够保证所有的数据以经被传送.如果成执行成功返回值是非0值,失败返回0

4文件的共享
加锁可以防止其他进程对该区域进行读取或写入操作,加锁与解锁使用LockFile与UnlockFile也可以使用LockFileEx与UnlockFileEx,这两个函数使用异步方式执行.

invoke LockFile,hFile,dwFileOffsetLow,dwFileOffsetHigh,nNumberOfBytesToLockLow,nNumberOfBytesToLockHigh
invoke UnlockFile,hFile,dwFileOffsetLow,dwFileOffsetHigh,nNumberOfBytesToLockLow,nNumberOfBytesToLockHigh

dwFileOffsetLow和dwFileOffsetHigh参数组合起来指定了加锁区域的开始位置
dwNumberOfBytesToLockLow和dwNumberOfBytesToLockHigh组合指定加锁区域的大小,windows中,可以只使用低32位
注意:不能对一个区域重复加锁,两个不同的加锁区域也不能重叠,在对文件加锁与解锁操作的区域也必须一一对应,必须一次全部解锁,不能分次解锁.当程序读取一个可能被其它时程加锁的程序时,如果读取失败,必须调用GetLastError来获取失败原因,如果是因为锁定造成的,那可以等待一段时间后继续读取.

其它内存管理函数

1.填充和移动内存

mov esi,offset szSource
mov edi,offset szDest
mov ecx,dwSize
cld
rep movsb

以上代码实现从szSource开始的dwSize大小的内存块移动到szDest处

xor eax,eax
mov edi,offset szDest
mov ecx,dwSize
cld
rep stosb

以上代码实现szDest处开始的dwSize字节填充为0,如果把xor eax,eax换成mov al,xx 那上边的代码将实现将内存块填充为xx

以上功能对应的API如下:

invoke RtlMoveMemory,offset szDest,offset szSource,dwSize ;移动内存
invoke RtlFillMemory,offset szDest,dwSize,dbFill ;以dbFill填充内存块
invoke RtlZeroMemory,offset szDest,dwSize ;以0填充内存块

2.内存状态测试

invoke IsBadCodePtr,lpMemory
invoke IsBadReadPtr,lpMemory,dwSize
invoke IsBadWritePtr,lpMemory,dwSize
invoke IsBadStringPtr,lpMemory,dwSize

IsBadCodePtr:函数测试指针指向的单个字节是否可读,如果可读返回0,否则返回非0值
IsBadReadPtr:测试某段内存是否可读,如果这段内存的所有字节全可读则返回0,否则返回非0值
IsBadWritePtr:测试某段内存是否可写,如果内存段全都可写则返回0,否则返回非0值
IsBadStringPtr:测试一个指向以0结尾的字符串是否可读,dwSize为字符串最大长度,如果字符串包含结尾的0全是可读的,返回0,否则返回非0值,缓冲区中剩余的字节则不予测试

堆管理函数

堆管理函数
1.私有堆的创建和释放
私有堆的创建

invoke HeapCreate,flOptions,dwInitialSize,dwMaximumSize
.if eax && (eax<0c0000000h)
mov hHeap,eax
.endif

flOptions指定堆属性,有两个值
HEAP_NO_SERIALIZE:标志私有堆不进行独占检测
HEAP_GENERATE_EXCEPTIONS:指定函数失败的返回值,不指定失败返回NULL
dwInitialSize指定创建堆时分配的物理内存
dwMaximumSize为堆自动扩展的最大值.为0则没有最大限制,非0时在堆中申请的内存块不能大于7FFF8h(524K)

私有堆的释放

invoke HeapDestroy,hHeap

将释放堆中所有的内存块.成功运行返回TRUE,当进程终止时系统也会自动调用此函数

2.堆中分配和释放内存块
堆中分配内存块

invoke HeapAlloc,hHeap,dwFlags,dwBytes
.if eax && (eax<0c0000000h)
mov lpMemory,eax
.endif

hHeap:是创建堆时的句柄
dwFlags有三个值
HEAP_NO_SERIALIZE:不进行独占检测,如果创建堆时用了,这里就可以省了
HEAP_GENERATE_EXCEPTIONS:失败返回出错原因,而不仅是一个NULL,创建堆时用了,这里也可以省了
HEAP_ZERO_MEMORY:将分配内存用0初始化
dwBytes:是要分配的内存块的字节数
分配成功返回值是指向内存块第一个字节的指针,失败返回错误代码或NULL,要看dwFlags
STATUS_NO_MEMORY:取值为0c0000017h表示内存不够
STATUS_ACCESS_VIOLATION:取值为0c0000005h表示参数不正确或堆被破坏
在堆中分配内存块只能是固定地址的内存块.

堆中释放内存块

invoke HeapFree,hHeap,dwFlags,lpMemory

hHeap:堆句柄
dwFlags:可以使用HEAP_NO_SERIALIZE
lpMemory:是HeapAlloc函数返回的内存块指针
成功返回非0值,失败返回0

堆中调整内存块大小

invoke HeapReAlloc,hHeap,dwFlags,lpMemory,dwBytes
.if eax && (eax<0c0000000h)
mov lpMemory,eax
.endif

hHeap:堆句柄
dwFlags四个值
HEAP_GENERATE_EXCEPTIONS:返回值
HEAP_NO_SERIALIZE:独占检测
HEAP_ZERO_MEMORY:初始化为0
HEAP_REALLOC_IN_PLACE_ONLY:分配内存时不可移动内存块,使用此标志,指针必定和原来相同
lpMemory:为堆中内存块的指针
dwBytes:要改变的内存块的大小的字节数

3.其它堆管理函数
HeapLock,HeapUnlock,GetProcessHeaps,GetProcessHeap,HeapCompact,HeapSize,
HeapValidate,HeapWalk.

invoke GetProcessHeaps,NumberOfHeaps,lpHeaps

NumberOfHeaps:指定了缓冲区可以存放句柄的数量
lpHeaps:是一个指针,指向用来接收堆句柄的缓冲区,缓冲区长度应该等于NumberOfHeaps*4
执行后函数返回进程中所有堆的句柄到缓冲区中,也包括默认堆的句柄

.repeat
invoke HeapWalk,hHeap,lpEntry
push eax
pop eax
.until !eax

hHeap:是堆的句柄
lpEntry:指向一个句含有PROCESS_HEAP_ENTRY结构的缓冲区
每次执行返回一个PROCESS_HEAP_ENTRY的结构块信息,如果还有其他的内存块,函数返回TRUE,程序一直循环调用直到返回FALSE为止,在多线程的程序中使用,必须先使用HeapLock函数,否则会调用失败.

invoke HeapValidate,hHeap,dwFlags,lpMemory

验证堆的完整性或堆中某个内存块的完整性
lpMemory为NULL函数顺序验证堆中所有的内存块,如果lpMemory指定了一个内存块,则只验证这个内存块
dwFlags:可以指定HEAP_NO_SERIALIZE,独占标志
如果所有内存块都完好无损,返回非0值,否则返回0

invoke HeapLock,hHeap
invoke HeapUnlock,hHeap

销定和解销堆,主要用于线程同步,成功返回非0值,否则返回0,在程序中一般用HEAP_NO_SERIALIZE来控制

invoke HeapCompact,hHeap,dwFlags

合并堆中的空闲内存块并释放不在使用中的内存页面

invoke HeapSize,hHeap,dwFlags,lpMemory

返回堆中某个内存块大小lpMemory指定了需要返回大小的内存块,成功返回内存块大小,失败返回-1

虚拟内存管理函数

虚拟内存管理函数
VirtualAlloc,VirtualFree – 地址空间分配和释放
VirtualLock,VirtualUnlock – 对内存页进行销定和解锁
VirtualQuery,VirtualQueryEx – 查询内存页的状态
VirtualProtect,VirtualProtectEx – 改变内存页的保护属性

1.保留和释放地址空间

invoke VirtualAlloc,lpAddress,dwSize,flAllocationType,flProtect

lpAddress:指定要保留或提交的内存地址,为NULL时系统自动分配一个地址
dwSize:表示函数分配的地址范围大小,单位字节
flAlloctiontype可以取的标志有三个
MEM_COMMIT:为指定地址空间提交物理内存
MEM_RESERVE:保留指定地址空间,不分配物理内存
MEM_TOP_DOWN:尽可能使用高端的地址空间
flProtect可以取的标志有六个
PAGE_READONLY:为以提交的物理内存的址空间设定只读属性
PAGE_READWRITE:为以提交的物理内存设定为可读写属性
PAGE_EXECUTE:设定为可执行属性
PAGE_EXECUTE_READ:为物理内存设定可读和可执行属性
PAGE_EXECUTE_READWRITE:为内存设定为可读写和可执行属性
PAGE_NOACCESS:为保留的地址空间设定为不可存取模式
函数执行成功会返回一个指向被保留地址范围开始位置的指针,执行失败返回NULL

invoke VirtualFree,lpAddress,dwSize,dwFreeType

lpAddress与dwSize:为指定地址和空间的大小,与VirtualAlloc相同
dwFreeType可以的标志有两个
MEM_DECOMMIT:为一个以提交的物理内存的地址空间解除提交
MEM_RELEASE:释放保留的址址空间

例:

invoke VirtualAlloc,NULL,10485760,MEM_RESERVE,PAGE_NOACCESS
.if eax
mov lpAddress,eax
.endif

空间的分配方式用MEM_RESERVE保留指定地址空间,不分配物理内存,是无法访问的,所以保护属性必须使用PAGE_NOACCESS,以上代码保留10M大小的空间,保留地址并不保证将来有可用内存来提交给这些地址

invoke VirtualFree,lpAddress,0,MEM_RELEASE

lpAddress是VirtualAlloc返回的指针,释放保留内存时dwSize必须为0,释放一个地址空间中的所有页面必须是同一个状态,否则释放操作会失败.

2.使用保留的地址空间

invoke VirtualAlloc,lpAddress,4096,MEM_COMMIT,PAGE_READWRITE
.if eax
mov lpMemory,eax
.endif

保留地址内存可以按一页的大小被分次提交,也可以一次提交所有的保留地址,当内存被提交时,可以被分配为物理内存页,也有可以被分配在页文件中,在提交时lpAddress不能为NULL,要指定一个特定的地址来准确地指示被保留的哪一页会被提交.属性可以设定成可访问的,成功返回第一页起始线程的地址,失败返回NULL

invoke VirtualAlloc,NULL,dwSize,MEM_RESERVE or MEM_COMMIT,PAGE_READWRITE
.if eax
mov lpMemory,eax
.endif

这种方法与GlobalAlloc函数直接分配一块内存没有多大区别,只是可以指定分配的内存块地址

invoke VirtualFree,lpMemory,dwSize,MEM_DECOMMIT

以上代码为解除提交,让它们从提交状态返回到保留状态,函数的操作对象是整个页面,如果指定的内存范围不是整个页面,函数会自动将整个范围同属一个页面的地址全部解除提交.

3.内存页的保护和锁定

invoke VirtualProtect,lpAddress,dwSize,flNewProtect,lpflOldProtect

flNewProtect:是新的保护方式,取值可以参考VirtualAlloc的flProtect的标志
lpOldProtect:是指向一个双字的指针,函数会在这里返回原来的保护方式,如果不需要可以设置为NULL

invoke VirtualLock,lpAddress,dwSize
invoke VirtualUnlock,lpAddress,dwSize

锁定的意思是将指定内存页保留在物理内存中,不许将它交换到磁盘页文件中,同时锁定的内数不能超过30个

超类化 罗老板的源码

super.asm:

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .386
    .model flat, stdcall
    option casemap :none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include    windows.inc
include    user32.inc
includelib  user32.lib
include    kernel32.inc
includelib  kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Equ 等值定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN  equ  1000
DLG_MAIN  equ  1000
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .data?

hInstance  dd  ?
hWinMain  dd  ?
lpOldProcEdit  dd  ?

    .const
szAllowedChar  db  '0123456789ABCDEFabcdef',08h
szEditClass  db  'Edit',0
szClass    db  'HexEdit',0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; HexEdit控件的新窗口过程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcEdit  proc  uses ebx edi esi hWnd,uMsg,wParam,lParam

    mov  eax,uMsg
    .if  uMsg ==  WM_CHAR
      mov  eax,wParam
      mov  edi,offset szAllowedChar
      mov  ecx,sizeof szAllowedChar
      repnz  scasb
      .if  ZERO?
        .if  al > '9'
          and  al,not 20h
        .endif
        invoke  CallWindowProc,lpOldProcEdit,hWnd,uMsg,eax,lParam
        ret
      .endif
    .else
      invoke  CallWindowProc,lpOldProcEdit,hWnd,uMsg,wParam,lParam
      ret
    .endif
    xor  eax,eax
    ret

_ProcEdit  endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 基于Edit类建立一个新的类:HexEdit
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_SuperClass  proc
    local  @stWC:WNDCLASSEX

    mov  @stWC.cbSize,sizeof @stWC
    invoke  GetClassInfoEx,NULL,addr szEditClass,addr @stWC
    push  @stWC.lpfnWndProc
    pop  lpOldProcEdit
    mov  @stWC.lpfnWndProc,offset _ProcEdit
    push  hInstance
    pop  @stWC.hInstance
    mov  @stWC.lpszClassName,offset szClass
    invoke  RegisterClassEx,addr @stWC
    ret

_SuperClass  endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcDlgMain  proc  uses ebx edi esi hWnd,wMsg,wParam,lParam

    mov  eax,wMsg
;********************************************************************
    .if  eax ==  WM_CLOSE
      invoke  EndDialog,hWnd,NULL
;********************************************************************
    .else
      mov  eax,FALSE
      ret
    .endif
    mov  eax,TRUE
    ret

_ProcDlgMain  endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&g

t;>>>>>>>>>>>>>>>>>>>
start:
    invoke  GetModuleHandle,NULL
    mov  hInstance,eax
    invoke  _SuperClass
    invoke  DialogBoxParam,hInstance,DLG_MAIN,NULL,offset _ProcDlgMain,NULL
    invoke  ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    end  start

super.rc:

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#include    <resource.h>
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#define  ICO_MAIN  1000
#define  DLG_MAIN  1000
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN  ICON    "Main.ico"
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DLG_MAIN DIALOG 107, 102, 126, 82
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "SuperClass"
FONT 9, "宋体"
{
CONTROL "",-1,"HexEdit",ES_LEFT | WS_BORDER | WS_TABSTOP,5,5,115,12
CONTROL "",-1,"HexEdit",ES_LEFT | WS_BORDER | WS_TABSTOP,5,20,115,12
CONTROL "",-1,"HexEdit",ES_LEFT | WS_BORDER | WS_TABSTOP,5,35,115,12
CONTROL "",-1,"HexEdit",ES_LEFT | WS_BORDER | WS_TABSTOP,5,50,115,12
CONTROL "",-1,"HexEdit",ES_LEFT | WS_BORDER | WS_TABSTOP,5,65,115,12
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

标准内存管理函数

标准内存管理函数
GlobalAlloc,GlobalFree,GlobalReAlloc,GlobalLock,GlobalUnlock,GlobalDiscard,GlobalFlags,GlobalHandle,GlobalSize

1.固定的内存块

invoke GlobalAlloc,GMEM_FIXED or GMEM_ZEROINIT,dwBytes
.if eax
mov lpMemory,eax
.endif

申请失败eax返回NULL,成功返回指向申请内存起始地址的指针.GPTR

释放固定内存块

invoke GlobalFree,lpMemory

释放成功返回NULL,返否返回输入的lpMemory

改变内存块大小

invoke GlobalReAlloc,lpMemory,dwBytes,uFlags
.if eax
mov lpNewMemory,eax
.endif

lpMemory是先前申请的内存指针,dwBytes是新大小,uFlags可以是空
uFlags来规定是否允许移动内存块,当为GMEM_MOVEABLE选项时,如果需要移动内存块windows会在其它地方新开一个内存块,并把原来的内容自动复制到新内存中,这时函数会返回一个新的指针,原来的指针作废
如果不指定GMEM_MOVEABLE,当后面的空间不足时,函数失败并返回NULL,原来的指针继续有效.
改变内存大小时,建意使用以下代码

invoke GlobalReAlloc,lpMemory,dwBytes,GMEM_ZEROINIT or GMEM_MOVEABLE
.if eax
mov lpMemory,eax
.endif

2.可移动的内存块
申请可移动的内存块

invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,dwBytes
.if eax
mov hMemory,eax
.endif

GMEM_MOVEABLE or GMEM_ZEROINIT = GHND
申请失败eax返回NULL,成功返回一个句柄.可移动内存块不能超过65536个,固定内存块数量无限制

销定可移动内存:

invoke GlobalLock,hMemory
.if eax
mov lpMemory,eax
.endif

销定失败返回NULL,成功返回一个指针
当不使用时要解销

invoke GlobalUnlock,hMemory

解销成功返回非0值

释放可移动内存块

invoke GlobalFree,hMemory

释放成功返回NULL

调整可移动内存块大小

invoke GlobalReAlloc,hMemory,dwBytes,GHND

如果成功返回hMemory,失败返回NULL
注意最好先解销再调整大小

3.可丢弃的内存块
可丢弃的内存块必须是可移动的内存块

invoke GlobalAlloc,GHND or GMEM_DISCARDABLE,dwBytes
.if eax
mov hMemory,eax
.endif

如果GlobalLock内存返回NULL,说明内存以被丢弃了,但句柄还是有效的,如果还要使用这个句柄,可以使用
GlobalReAlloc来重新分配内存,当销定计数为0时,可以使用GlobalDiscard主动将它丢弃.

4.获取内存块的信息
GlobalFlags主要用来取得可移动内存的计数,也可以检测可丢弃内存是否被丢弃.

invoke GlobalFlags,hMemory
and eax,GMEM_LOCKCOUNT
mov dwLockCount,eax

返回值不是GMEM_INVALID_HANDLE说明调用成功,这时返回值低8位是内存块的销定计数
GMEM_DISCARDABLE 表示内存块是可丢弃内存块
GMEM_DISCARDED 表示内存块以被失弃

GlobalHandle可以从GlobalLock得到的lpMemory值获取对应的hMemory
GlobalSize可以获取一个内存块的尺寸

子类化 罗老板的源码

subclass.asm:

    .386
    .model flat, stdcall
    option casemap :none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include    windows.inc
include    user32.inc
includelib  user32.lib
include    kernel32.inc
includelib  kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Equ 等值定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN  equ  1000
DLG_MAIN  equ  1000
IDC_HEX    equ  1001
IDC_DEC    equ  1002
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .data?
hInstance  dd  ?
hWinMain  dd  ?
dwOption  dd  ?
lpOldProcEdit  dd  ?

    .const
szFmtDecToHex  db  '%08X',0
szFmtHexToDec  db  '%u',0
szAllowedChar  db  '0123456789ABCDEFabcdef',08h
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; IDC_HEX编辑框的新窗口过程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcEdit  proc  uses ebx edi esi hWnd,uMsg,wParam,lParam

    mov  eax,uMsg
    .if  uMsg ==  WM_CHAR
      mov  eax,wParam
      mov  edi,offset szAllowedChar
      mov  ecx,sizeof szAllowedChar
      repnz  scasb
      .if  ZERO?
        .if  al > '9'
          and  al,not 20h
        .endif
        invoke  CallWindowProc,lpOldProcEdit,hWnd,uMsg,eax,lParam
        ret
      .endif
    .else
      invoke  CallWindowProc,lpOldProcEdit,hWnd,uMsg,wParam,lParam
      ret
    .endif
    xor  eax,eax
    ret

_ProcEdit  endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 计算16进制到10进制
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_HexToDec  proc
    local  @szBuffer[512]:byte

    invoke  GetDlgItemText,hWinMain,IDC_HEX,addr @szBuffer,sizeof @szBuffer
    lea  esi,@szBuffer
    cld
    xor  eax,eax
    mov  ebx,16
    .while  TRUE
      movzx  ecx,byte ptr [esi]
      inc  esi
      .break  .if ! ecx
      .if  cl > '9'
        sub  cl,'A' – 0ah
      .else
        sub  cl,'0'
      .endif
      mul  ebx
      add  eax,ecx
    .endw
    invoke  wsprintf,addr @szBuffer,addr szFmtHexToDec,eax
    invoke  SetDlgItemText,hWinMain,IDC_DEC,addr @szBuffer
    ret

_HexToDec  endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 计算10进制到16进制
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_DecToHex  proc
    local  @szBuffer[512]:byte

    invoke  GetDlgItemInt,hWinMain,IDC

_DEC,NULL,FALSE
    invoke  wsprintf,addr @szBuffer,addr szFmtDecToHex,eax
    invoke  SetDlgItemText,hWinMain,IDC_HEX,addr @szBuffer
    ret

_DecToHex  endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcDlgMain  proc  uses ebx edi esi hWnd,wMsg,wParam,lParam

    mov  eax,wMsg
;********************************************************************
    .if  eax ==  WM_CLOSE
      invoke  EndDialog,hWnd,NULL
;********************************************************************
    .elseif  eax ==  WM_INITDIALOG
      mov  eax,hWnd
      mov  hWinMain,eax
      invoke  SendDlgItemMessage,hWnd,IDC_HEX,EM_LIMITTEXT,8,0
      invoke  SendDlgItemMessage,hWnd,IDC_DEC,EM_LIMITTEXT,10,0
      invoke  GetDlgItem,hWnd,IDC_HEX
      invoke  SetWindowLong,eax,GWL_WNDPROC,addr _ProcEdit
      mov  lpOldProcEdit,eax
;********************************************************************
    .elseif  eax ==  WM_COMMAND
      mov  eax,wParam
      .if  ! dwOption
        mov  dwOption,TRUE
        .if  ax ==  IDC_HEX
          invoke  _HexToDec
        .elseif  ax ==  IDC_DEC
          invoke  _DecToHex
        .endif
        mov  dwOption,FALSE
      .endif
    .else
      mov  eax,FALSE
      ret
    .endif
    mov  eax,TRUE
    ret

_ProcDlgMain  endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
    invoke  GetModuleHandle,NULL
    mov  hInstance,eax
    invoke  DialogBoxParam,hInstance,DLG_MAIN,NULL,offset _ProcDlgMain,NULL
    invoke  ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    end  start

subclass.rc

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#include    <resource.h>
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#define  ICO_MAIN  1000
#define  DLG_MAIN  1000
#define IDC_HEX    1001
#define IDC_DEC    1002
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN  ICON    "Main.ico"
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DLG_MAIN DIALOG 107, 102, 129, 42
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Hex <> Dec"
FONT 9, "宋体"
{
LTEXT "Hex", -1, 7, 9, 15, 8
EDITTEXT IDC_HEX, 27, 7, 94, 12
LTEXT "Dec", -1, 7, 26, 15, 8
EDITTEXT IDC_DEC, 27, 24, 94, 12, ES_NUMBER
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

QueryPerformanceCounter 微秒计数器

QueryPerformanceCounter 可以取得开机以来64位的时间计数值
QueryPerformanceFrequency 取得计算机每秒钟的计数值,和CPU速度有关

Timer.asm:

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .386
    .model flat,stdcall
    option casemap:none
; Include 文件定义
include    windows.inc
include    user32.inc
includelib  user32.lib
include    kernel32.inc
includelib  kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ID_TIMER1  equ  1
DLG_MAIN  equ  1
IDC_COUNT  equ  101
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
dw1m    dd 1000000
ddcc1  db "%d 微秒", 0
buffer db 512 dup(?)

num1 dq 333
num2 dq 3
res dd 0

    .data?
hInstance  dd    ?
tick1    dd    ?
dqtick1  dq  ?
dqtick2  dq  ?
dqFreq  dq  ?
dqTime  dq  ?
buf db 200 dup(?)
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 计算过程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

_ProcDlgMain  proc  uses ebx edi esi,hWnd,uMsg,wParam,lParam
    mov  eax,uMsg
    .if  eax ==  WM_TIMER
      mov  eax,wParam
      .if  eax == ID_TIMER1
        invoke  MessageBeep,-1
      .endif
    .elseif  eax ==  WM_INITDIALOG
      invoke QueryPerformanceCounter,addr dqtick1
      .while 1
        add edi,1
        .break .if edi > 100000000
        ;.continue
      .endw
      invoke QueryPerformanceCounter,addr dqtick2
      invoke QueryPerformanceFrequency,addr dqFreq
      mov eax,DWORD ptr dqtick1
      mov edx,DWORD ptr dqtick1+4
      sub DWORD ptr dqtick2,eax
      sbb DWORD ptr dqtick2+4,edx
      finit
      fild dqFreq
      fild dqtick2
      fimul dw1m
      fdivr
      fistp dqTime
      invoke wsprintf,addr buf,addr ddcc1,dqTime
      invoke SendDlgItemMessage,hWnd,IDC_COUNT,WM_SETTEXT,NULL,addr buf
    .elseif eax ==  WM_COMMAND
      mov  eax,wParam
      .if ax == IDOK
      invoke QueryPerformanceCounter,addr dqtick1
      .while 1
        add edi,1
        .break .if edi > 100000000
        ;.continue
      .endw
      invoke QueryPerformanceCounter,addr dqtick2
      invoke QueryPerformanceFrequency,addr dqFreq
      mov eax,DWORD ptr dqtick1
      mov edx,DWORD ptr dqtick1+4
      sub DWORD ptr dqtick2,eax
      sbb DWORD ptr dqtick2+4,edx
      finit
      fild dqFreq
      fild dqtick2
      fimul dw1m
      fdivr
      fistp dqTime
      invoke wsprintf,addr buf,addr ddcc1,dqTime
      invoke SendDlgItemMessage,hWnd,IDC_COUNT,WM_SETTEXT,NULL,addr buf
      .endif
    .elseif  eax ==  WM_CLOSE
      invoke  EndDialog,hWnd,NULL
    .else
      mov  eax,FALSE
      ret
    .endif
    mov  eax,TRUE
    ret

_ProcDlgMain  endp

start:
    invoke  GetModuleHandle,NULL
    mov  hInstance,eax
    invoke  DialogBoxParam,hInstance,DLG_MAIN,NULL,offset _ProcDlgMain,NULL
    invoke  ExitProcess,NULL
    end  start

Timer.rc:

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&

gt;>>>>>>>>>>>>>>>>>>>>>
#include <resource.h>
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#define DLG_MAIN 1
#define ICO_1 1
#define IDC_COUNT 101
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_1 ICON "1.ico"
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DLG_MAIN DIALOG 70, 110, 120, 70
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "1+1 1亿次 小杰的博客"
FONT 9, "宋体"
{
LTEXT "计数:", -1, 35, 16, 25, 10
LTEXT "", IDC_COUNT, 62, 16, 60, 10
DEFPUSHBUTTON "开始", IDOK, 35, 36, 50, 20
}

GetTickCount 计算1+1 1亿次的时间

GetTickCount最小计数时间在15MS左右

timer.asm:

    .386
    .model flat,stdcall
    option casemap:none
; Include 文件定义
include    windows.inc
include    user32.inc
includelib  user32.lib
include    kernel32.inc
includelib  kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ID_TIMER1  equ  1
DLG_MAIN  equ  1
IDC_COUNT  equ  101
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .data?
hInstance  dd    ?
tick1    dd    ?
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 计算过程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

_ProcDlgMain  proc  uses ebx edi esi,hWnd,uMsg,wParam,lParam
    mov  eax,uMsg
    .if  eax ==  WM_TIMER
      mov  eax,wParam
      .if  eax == ID_TIMER1
        invoke  MessageBeep,-1
      .endif
    .elseif  eax ==  WM_INITDIALOG
      invoke GetTickCount
      mov tick1,eax
      .while 1
        add edi,1
        .break .if edi > 100000000
        ;.continue
      .endw
      invoke GetTickCount
      sub eax,tick1
      invoke  SetDlgItemInt,hWnd,IDC_COUNT,eax,FALSE
    .elseif eax ==  WM_COMMAND
      mov  eax,wParam
      .if ax == IDOK
        invoke GetTickCount
        mov tick1,eax
        .while 1
          add edi,1
          .break .if edi > 100000000
          ;.continue
        .endw
        invoke GetTickCount
        sub eax,tick1
        invoke  SetDlgItemInt,hWnd,IDC_COUNT,eax,FALSE
      .endif
    .elseif  eax ==  WM_CLOSE
      invoke  EndDialog,hWnd,NULL
    .else
      mov  eax,FALSE
      ret
    .endif
    mov  eax,TRUE
    ret

_ProcDlgMain  endp

start:
    invoke  GetModuleHandle,NULL
    mov  hInstance,eax
    invoke  DialogBoxParam,hInstance,DLG_MAIN,NULL,offset _ProcDlgMain,NULL
    invoke  ExitProcess,NULL
    end  start

timer.rc:

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#include    <resource.h>
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#define  DLG_MAIN    1
#define  ICO_1      1
#define  IDC_COUNT    101
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_1  ICON    "1.ico"
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DLG_MAIN DIALOG 70, 110, 120, 70
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "1+1 1亿次 小杰的博客"
FONT 9, "宋体"
{
LTEXT "计数:", -1, 35, 16, 25, 10
LTEXT "", IDC_COUNT, 62, 16, 40, 10
DEFPUSHBUTTON "开始", IDOK, 35, 36, 50, 20
}

一个秒表的例子(汇编代码)

WM_TIME是一个低级消息,只有窗口的消息队列中没有其它消息才会发送WM_TIME消息,否则不会处理,过后不会重新发送.

time.asm代码:

    .386
    .model flat,stdcall
    option casemap:none
; Include 文件定义
include    windows.inc
include    user32.inc
includelib  user32.lib
include    kernel32.inc
includelib  kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ID_TIMER1  equ  1
DLG_MAIN  equ  1
IDC_COUNT  equ  101
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .data?
hInstance  dd    ?
hWinMain  dd    ?
idTimer    dd    ?
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 定时器过程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcTimer  proc  _hWnd,_uMsg,_idEvent,_dwTime
    pushad
    invoke  GetDlgItemInt,hWinMain,IDC_COUNT,NULL,FALSE
    inc  eax
    invoke  SetDlgItemInt,hWinMain,IDC_COUNT,eax,FALSE
    popad
    ret
_ProcTimer  endp

_ProcDlgMain  proc  uses ebx edi esi,hWnd,uMsg,wParam,lParam
    mov  eax,uMsg
    .if  eax ==  WM_TIMER
      mov  eax,wParam
      .if  eax == ID_TIMER1
        invoke  MessageBeep,-1
      .endif
    .elseif  eax ==  WM_INITDIALOG
      push  hWnd
      pop  hWinMain
      invoke  SetTimer,hWnd,ID_TIMER1,2000,NULL
      invoke  SetTimer,NULL,NULL,1000,addr _ProcTimer
      mov  idTimer,eax
    .elseif  eax ==  WM_CLOSE
      invoke  KillTimer,hWnd,ID_TIMER1
      invoke  KillTimer,NULL,idTimer
      invoke  EndDialog,hWnd,NULL
    .else
      mov  eax,FALSE
      ret
    .endif
    mov  eax,TRUE
    ret

_ProcDlgMain  endp

start:
    invoke  GetModuleHandle,NULL
    mov  hInstance,eax
    invoke  DialogBoxParam,hInstance,DLG_MAIN,NULL,offset _ProcDlgMain,NULL
    invoke  ExitProcess,NULL
    end  start

time.rc代码:

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#include    <resource.h>
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#define  DLG_MAIN    1
#define  ICO_1      1
#define  IDC_COUNT    101
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_1  ICON    "1.ico"
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DLG_MAIN DIALOG 70, 110, 113, 40
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "计时器 小杰的博客"
FONT 9, "宋体"
{
LTEXT "计数:", -1, 35, 16, 25, 10
LTEXT "", IDC_COUNT, 62, 16, 40, 10
}

源码下载:
点击下载