汇编(MOV,SUB,PUSH,POP,...)

汇编开发(九):MS-Windows 程序

2019-02-24  本文已影响0人  _凌浩雨

1. Win32 控制台程序

1). 显示一个消息框
MessageBoxA PROTO,
    hWnd:DWORD, ; handle to window (can be null)
    lpText:PTR BYTE, ; 消息框内部
    lpCaption:PTR BYTE, ; 消息框标题
    uType:DWORD ; 内容和行为

hWnd在控制台应用中可以设置为NULL
lpText为一个以空值终止的字符串指针
lpCaotion为一个以空值终止的对话框标题字符串指针
uType为消息框的内容和行为。

示例:

; 消息框

INCLUDE Irvine32.inc

.data
    captionW    BYTE "Warning",0
    warningMsg  BYTE "The current operation may take years "
                BYTE "to complete.",0

    captionQ    BYTE "Question",0
    questionMsg BYTE "A matching user account was not found."
                BYTE 0dh,0ah,"Do you wish to continue?",0

    captionC    BYTE "Information",0
    infoMsg     BYTE "Select Yes to save a backup file "
                BYTE "before continuing,",0dh,0ah
                BYTE "or click Cancel to stop the operation",0

    captionH    BYTE "Cannot View User List",0
    haltMsg     BYTE "This operation not supported by your "
                BYTE "user account.",0
    
.code 
main PROC
    ; 显示一个带图标的OK按钮
    INVOKE MessageBox, NULL, ADDR warningMsg, ADDR captionW, MB_OK + MB_ICONEXCLAMATION

    ; 显示一个带问号的图标和Yes/No按钮
    INVOKE MessageBox, NULL, ADDR questionMsg, ADDR captionQ, MB_YESNO + MB_ICONQUESTION
    
    ; 显示一个消息图标和Yes/No/Cancel按钮
    INVOKE MessageBox, NULL, ADDR infoMsg, ADDR captionC, MB_YESNOCANCEL + MB_ICONINFORMATION + MB_DEFBUTTON2

    ; 显示一个停止图标和一个OK按钮
    INVOKE MessageBox, NULL, ADDR haltMsg, ADDR captionH, MB_OK + MB_ICONSTOP

    exit
main ENDP
END
效果.gif
2). 控制台输入
ReadConsole PROTO,
    hConsoleInput:HANDLE,           ; input handle
    lpBuffer:PTR BYTE,              ; 缓冲指针
    nNumberOfCharsToRead:DWORD,     ; 要读取的字符数
    lpNumberOfCharsRead:PTR DWORD,  ; 要读取的字符指针
    lpReserved:DWORD                ; (不使用)

hConsoleInput一个有效的控制台输入句柄,使用GetStdHandle函数获取
lpBuffer字符数组首地址
nNumberOfCharsToRead要读取的字符数
lpNumberOfCharsRead要读取的字符指针
lpReserved保留参数

示例:

; 从控制台读取数据

INCLUDE Irvine32.inc

BufSize = 80

.data
    buffer BYTE BufSize DUP(?), 0, 0
    stdInHandle HANDLE ?
    bytesRead DWORD ?

.code 
main PROC
    ; 获取标准输入句柄
    INVOKE GetStdHandle, STD_INPUT_HANDLE
    mov stdInHandle, eax

    ; 等待用户输入
    INVOKE ReadConsole, stdInHandle, ADDR buffer, BufSize, ADDR bytesRead, 0
    ; 显示缓冲内容
    mov esi, OFFSET buffer
    mov ecx, bytesRead
    mov ebx, TYPE buffer
    call DumpMem

    call WaitMsg
    call Crlf
    exit
main ENDP
END
效果.png
.data
    messageId DWORD ?
.code
    call GetLastError
    mov messageId,eax

获取到错误码之后,我们可以调用FormatMessage 函数获取具体信息

FormatMessage PROTO,    ; format a message
    dwFlags:DWORD,      ; formatting options
    lpSource:DWORD,     ; location of message def
    dwMsgID:DWORD,      ; message identifier
    dwLanguageID:DWORD, ; language identifier
    lpBuffer:PTR BYTE,  ; ptr to buffer receiving string
    nSize:DWORD,        ; buffer size
    va_list:DWORD       ; pointer to list of arguments

其中除了lpBuffer是输出参数,其余均为输入参数。

dwFlags是一个整型的格式选项。
lpSource是一个消息的地址指针。
dwMsgID是通过GetLastError获取到的双字整型值。
dwLanguageID是语言标识。如果设置为0,则消息将是用户默认的语言
lpBuffer是一个以空值终止的字符串指针。
nSize是可以使用一个特殊的缓冲来获取消息。
va_list是格式消息字符串的指针。

示例:

.data
    messageId DWORD ?
    pErrorMsg DWORD ? ; points to error message
.code
    call GetLastError
    mov messageId,eax
    INVOKE FormatMessage, FORMAT_MESSAGE_ALLOCATE_BUFFER + \
    FORMAT_MESSAGE_FROM_SYSTEM, NULL, messageID, 0,
    ADDR pErrorMsg, 0, NULL
;----------------------------------------------------
WriteWindowsMsg PROC USES eax edx
;
; Displays a string containing the most recent error
; generated by MS-Windows.
; Receives: nothing
; Returns: nothing
;----------------------------------------------------
.data
    WriteWindowsMsg_1 BYTE "Error ",0
    WriteWindowsMsg_2 BYTE ": ",0
    pErrorMsg DWORD ?           ; points to error message
    messageId DWORD ?
.code
    call GetLastError
    mov messageId,eax
    ; Display the error number.
    mov edx,OFFSET WriteWindowsMsg_1
    call WriteString
    call WriteDec
    mov edx,OFFSET WriteWindowsMsg_2
    call WriteString
    ; Get the corresponding message string.
    INVOKE FormatMessage, FORMAT_MESSAGE_ALLOCATE_BUFFER + \
     FORMAT_MESSAGE_FROM_SYSTEM, NULL, messageID, NULL,
     ADDR pErrorMsg, NULL, NULL
    ; Display the error message generated by MS-Windows.
    mov edx,pErrorMsg
    call WriteString
    ; Free the error message string.
    INVOKE LocalFree, pErrorMsg
    ret
WriteWindowsMsg ENDP
Keyboard Control Key State Values.png
; 测试ReadKey

INCLUDE Irvine32.inc
INCLUDE Macros.inc

.code 
main PROC
L1:
    mov eax, 10             ; 延时消息
    call Delay
    call ReadKey            ; 等待输入
    jz L1
    test ebx, CAPSLOCK_ON
    jz L2
    mWrite <"CapsLock is ON", 0dh, 0ah>
    jmp L3
L2:
    mWrite <"CapsLock is OFF", 0dh, 0ah>
L3: exit

main ENDP
main
GetKeyState PROTO, nVirtKey:DWORD

示例:

; Keyboard Toggle Keys (Keybd.asm)
INCLUDE Irvine32.inc
INCLUDE Macros.inc
; GetKeyState sets bit 0 in EAX if a toggle key is
; currently on (CapsLock, NumLock, ScrollLock).
; It sets the high bit of EAX if the specified key is
; currently down.
.code
main PROC
    INVOKE GetKeyState, VK_NUMLOCK
    test al,1
    .IF !Zero?
        mWrite <"The NumLock key is ON",0dh,0ah>
    .ENDIF
    INVOKE GetKeyState, VK_LSHIFT
    test eax,80000000h
    .IF !Zero?
        mWrite <"The Left Shift key is currently DOWN",0dh,0ah>
    .ENDIF
    exit
main ENDP
END main
Testing Keys with GetKeyState.png
3). 控制台输出
WriteConsole PROTO,
    hConsoleOutput:HANDLE,
    lpBuffer:PTR BYTE,
    nNumberOfCharsToWrite:DWORD,
    lpNumberOfCharsWritten:PTR DWORD,
    lpReserved:DWORD

hConsoleOutput控制台输出流句柄
lpBuffer用户想要输出的数组指针
nNumberOfCharsToWrite数组长度
lpNumberOfCharsWritten指向一个整数,该整数分配了函数返回时实际写入的字节数。
lpReserved保留参数。

示例:

; 程序调用的Win32 控制台函数: GetStdHandle, ExitProcess, WriteConsole

INCLUDE Irvine32.inc

.data
    endl EQU <0dh,0ah> ; end of line sequence
    message LABEL BYTE
    BYTE "This program is a simple demonstration of"
    BYTE "console mode output, using the GetStdHandle"
    BYTE "and WriteConsole functions.",endl
    messageSize DWORD ($ - message)
    consoleHandle HANDLE 0 ; handle to standard output device
    bytesWritten DWORD ? ; number of bytes written
.code
main PROC
    ; 获取控制台输出句柄
    INVOKE GetStdHandle, STD_OUTPUT_HANDLE
    mov consoleHandle, eax

    ; 在控制台输出一个字符串
    INVOKE WriteConsole,
        consoleHandle,
        ADDR message,
        messageSize,
        ADDR bytesWritten,
        0
    call WaitMsg
    INVOKE ExitProcess, 0
main ENDP
END
WriteConsoleOutputCharacter PROTO,
    hConsoleOutput:HANDLE,              ; console output handle
    lpCharacter:PTR BYTE,               ; pointer to buffer
    nLength:DWORD,                      ; size of buffer
    dwWriteCoord:COORD,                 ; first cell coordinates
    lpNumberOfCharsWritten:PTR DWORD    ; output count
4). 读写文件

CreateFile函数可以创建新文件或打开现有文件。 如果成功,则返回打开文件的句柄; 否则,它返回一个名为INVALID_HANDLE_VALUE的特殊常量。

CreateFile PROTO,           ; create new file
    lpFilename:PTR BYTE,    ; ptr to filename
    dwDesiredAccess:DWORD,  ; access mode
    dwShareMode:DWORD,      ; share mode
    lpSecurityAttributes:DWORD,     ; ptr security attrib
    dwCreationDisposition:DWORD,    ; file creation options
    dwFlagsAndAttributes:DWORD,     ; file attributes
    hTemplateFile:DWORD             ; handle to template file
CreateFile Parameters.png

dwDesiredAccess: 允许您指定对文件的读访问,写访问,读/写访问或设备查询访问。

dwDesiredAccess Parameter Options.png

dwCreationDisposition: 指定对存在的文件执行的操作以及文件不存在时要执行的操作。

dwCreationDisposition Parameter Options.png

dwFlagsAndAttributes: 除了所有其他文件属性都覆盖FILE_ATTRIBUTE_NORMAL之外,任何属性组合都是可接受的。

Selected FlagsAndAttributes Values.png

示例:
—— 读取一个已存在的文件

INVOKE CreateFile,
    ADDR filename,  ; ptr to filename
    GENERIC_READ,   ; read from the file
    DO_NOT_SHARE,   ; share mode
    NULL,           ; ptr to security attributes
    OPEN_EXISTING,  ; open an existing file
    FILE_ATTRIBUTE_NORMAL,  ; normal file attribute
    0               ; not used

—— 为已存在的文件写内容

INVOKE CreateFile,
    ADDR filename,
    GENERIC_WRITE, ; write to the file
    DO_NOT_SHARE,
    NULL,
    OPEN_EXISTING, ; file must exist
    FILE_ATTRIBUTE_NORMAL,
    0

—— 创建一个文件,并设置普通属性,擦除已有的文件内容

INVOKE CreateFile,
    ADDR filename,
    GENERIC_WRITE, ; write to the file
    DO_NOT_SHARE,
    NULL,
    CREATE_ALWAYS, ; overwrite existing file
    FILE_ATTRIBUTE_NORMAL,
    0

—— 如果文件不存在则创建新文件,如果存在则打开

INVOKE CreateFile,
    ADDR filename,
    GENERIC_WRITE,  ; write to the file
    DO_NOT_SHARE,
    NULL,
    CREATE_NEW,     ; don't erase existing file
    FILE_ATTRIBUTE_NORMAL,
    0
CloseHandle PROTO,
    hObject:HANDLE ; handle to object
ReadFile PROTO,
    hFile:HANDLE,                   ; input handle
    lpBuffer:PTR BYTE,              ; ptr to buffer
    nNumberOfBytesToRead:DWORD,     ; num bytes to read
    lpNumberOfBytesRead:PTR DWORD,  ; bytes actually read
    lpOverlapped:PTR DWORD          ; ptr to asynch info

hFile是一个CreateFile函数返回的文件句柄
lpBuffer是从文件中接收到的数据指针
nNumberOfBytesToRead指定从文件中读取的最大字节数
lpNumberOfBytesRead指向一个整数,表示函数返回时实际读取的字节数
lpOverlapped应该设置为NULL(0)进行同步读取(我们使用)。 如果函数失败,则返回值为零。

WriteFile PROTO,
    hFile:HANDLE,       ; output handle
    lpBuffer:PTR BYTE,  ; pointer to buffer
    nNumberOfBytesToWrite:DWORD,        ; size of buffer
    lpNumberOfBytesWritten:PTR DWORD,   ; num bytes written
    lpOverlapped:PTR DWORD  ; ptr to asynch info

hFile是已打开文件的句柄
lpBuffer是要写入文件内容指针
nNumberOfBytesToWrite是指定写入文件的字节数
lpNumberOfBytesWritten是指向一个整数,该整数指定函数执行后实际写入的字节数
lpOverlapped对于同步操作,lpOverlapped应设置为NULL。 如果函数失败,则返回值为零

SetFilePointer PROTO,
    hFile:HANDLE,           ; file handle
    lDistanceToMove:SDWORD, ; bytes to move pointer
    lpDistanceToMoveHigh:PTR SDWORD, ; ptr bytes to move, high
    dwMoveMethod:DWORD      ; starting point

dwMoveMethod 移动指针的起始位置,有三个可选常量FILE_BEGIN, FILE_CURRENT, and FILE_END。

示例:

INVOKE SetFilePointer,
    fileHandle, ; file handle
    0,          ; distance low
    0,          ; distance high
    FILE_END    ; move method
5). Irvine32 库中的文件I/O操作

CreateOutputFile, OpenFile, WriteToFile, ReadFromFile, and CloseFile 源码如下:

;------------------------------------------------------
CreateOutputFile PROC
;
; Creates a new file and opens it in output mode.
; Receives: EDX points to the filename.
; Returns: If the file was created successfully, EAX
; contains a valid file handle. Otherwise, EAX
; equals INVALID_HANDLE_VALUE.
;------------------------------------------------------
    INVOKE CreateFile,
        edx, GENERIC_WRITE, DO_NOT_SHARE, NULL,
        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0
    ret
CreateOutputFile ENDP

;------------------------------------------------------
OpenFile PROC
;
; Opens a new text file and opens for input.
; Receives: EDX points to the filename.
; Returns: If the file was opened successfully, EAX
; contains a valid file handle. Otherwise, EAX equals
; INVALID_HANDLE_VALUE.
;------------------------------------------------------
    INVOKE CreateFile,
        edx, GENERIC_READ, DO_NOT_SHARE, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0
    ret
OpenFile ENDP

;--------------------------------------------------------
WriteToFile PROC
;
; Writes a buffer to an output file.
; Receives: EAX = file handle, EDX = buffer offset,
; ECX = number of bytes to write
; Returns: EAX = number of bytes written to the file.
; If the value returned in EAX is less than the
; argument passed in ECX, an error likely occurred.
;--------------------------------------------------------
.data
    WriteToFile_1 DWORD ?   ; number of bytes written
.code
    INVOKE WriteFile,       ; write buffer to file
    eax, ; file handle
    edx, ; buffer pointer
    ecx, ; number of bytes to write
    ADDR WriteToFile_1,     ; number of bytes written
    0 ; overlapped execution flag
    mov eax,WriteToFile_1   ; return value
    ret
WriteToFile ENDP

;--------------------------------------------------------
ReadFromFile PROC
;
; Reads an input file into a buffer.
; Receives: EAX = file handle, EDX = buffer offset,
; ECX = number of bytes to read
; Returns: If CF = 0, EAX = number of bytes read; if
; CF = 1, EAX contains the system error code returned
; by the GetLastError Win32 API function.
;--------------------------------------------------------
.data
    ReadFromFile_1 DWORD ?  ; number of bytes read
.code
    INVOKE ReadFile,
        eax,                ; file handle
        edx,                ; buffer pointer
        ecx,                ; max bytes to read
        ADDR ReadFromFile_1,; number of bytes read
        0                   ; overlapped execution flag
    mov eax,ReadFromFile_1
    ret
ReadFromFile ENDP

;--------------------------------------------------------
CloseFile PROC
;
; Closes a file using its handle as an identifier.
; Receives: EAX = file handle
; Returns: EAX = nonzero if the file is successfully
; closed.
;--------------------------------------------------------
    INVOKE CloseHandle, eax
    ret
CloseFile ENDP
6). 测试文件I/O程序
; 创建文件, 生成的文件在项目的根目录下
INCLUDE Irvine32.inc

BUFFER_SIZE = 501

.data   
    buffer BYTE BUFFER_SIZE DUP(?)
    filename BYTE "output.txt", 0
    fileHandle HANDLE ?
    stringLength DWORD ?
    bytesWritten DWORD ?
    str1 BYTE "Cannot create file", 0dh, 0ah, 0
    str2 BYTE "Bytes written to file [output.txt]: ", 0
    str3 BYTE "Enter up to 500 characters and press"
         BYTE "[Enter]: ", 0dh, 0ah, 0
.code
main PROC
    ; 创建一个新的文本文件
    mov edx, OFFSET filename
    call CreateOutputFile
    mov fileHandle, eax

    ; 检查错误
    cmp eax, INVALID_HANDLE_VALUE   ; 是否有错误
    jne file_ok                     ; no: skip
    mov edx, OFFSET str1            ; 显示错误
    call WriteString
    jmp quit

file_ok:
    ; 让用户输入字符串
    mov edx, OFFSET str3            ; "Enter up to ..."
    call WriteString
    mov ecx, BUFFER_SIZE            ; 输入是字符串
    mov edx, OFFSET buffer
    call ReadString
    mov stringLength, eax           ; 计算字符串的字符数

    ; 写入文件
    mov eax, fileHandle
    mov edx, OFFSET buffer
    mov ecx, stringLength
    call WriteToFile
    mov bytesWritten, eax           ; 保存返回值
    call CloseFile
    
    ; 显示返回值
    mov edx, OFFSET str2            ; Bytes written
    call WriteString
    mov eax, bytesWritten
    call WriteDec
    call Crlf

quit:
    call WaitMsg
    call Crlf
    exit

main ENDP
END
; 读取文件内容

INCLUDE Irvine32.inc
INCLUDE macros.inc

BUFFER_SIZE = 5000

.data 
    buffer BYTE BUFFER_SIZE DUP(?)
    filename BYTE 80 DUP(0)
    fileHandle HANDLE ?

.code 
main PROC
    ; 让用户输入文件名
    mWrite "Enter an input filename: "
    mov edx, OFFSET filename
    mov ecx, SIZEOF filename
    call ReadString

    ; 打开文件
    mov edx,OFFSET filename
    call OpenInputFile
    mov fileHandle, eax

    ; 检查错误
    cmp eax, INVALID_HANDLE_VALUE
    jne file_ok
    mWrite <"Cannot open file", 0dh, 0ah>
    jmp quit

file_ok:
    ; 读取文件内容到缓冲区
    mov edx, OFFSET buffer
    mov ecx, BUFFER_SIZE
    call ReadFromFile
    jnc check_buffer_size
    mWrite "Error reading file."
    call WriteWindowsMsg
    jmp close_file

check_buffer_size:
    cmp eax, BUFFER_SIZE
    jb buf_size_ok
    mWrite <"Error: Buffer too small for the file", 0dh, 0ah>
    jmp quit
buf_size_ok:
    mov buffer[eax], 0
    mWrite "File size: "
    call WriteDec
    call Crlf
    ; 显示缓冲区内容
    mWrite <"Buffer: ", 0dh, 0ah, 0dh, 0ah>
    mov edx, OFFSET buffer
    call WriteString
    call Crlf
close_file:
    mov eax, fileHandle
    call CloseFile
quit:
    call WaitMsg
    call Crlf
    exit
main ENDP
END
7). 控制台窗口操作

Win32 API提供了对控制台窗口及其缓冲区的可控制.

Screen buffer and console window.png

有几个函数会影响控制台窗口及其相对于屏幕缓冲区的位置:
—— SetConsoleWindowInfo设置控制台窗口相对于屏幕缓冲区的大小和位置。
—— GetConsoleScreenBufferInfo返回控制台窗口相对于屏幕缓冲区的矩形坐标(以及其他内容)。
—— SetConsoleCursorPosition将光标位置设置为屏幕缓冲区内的任何位置; 如果该区域不可见,则移动控制台窗口以使光标可见。
—— ScrollConsoleScreenBuffer移动屏幕缓冲区中的部分或全部文本,这会影响控制台窗口中显示的文本

.data
    titleStr BYTE "Console title",0
.code
    INVOKE SetConsoleTitle, ADDR titleStr
GetConsoleScreenBufferInfo PROTO,
    hConsoleOutput:HANDLE,
    lpConsoleScreenBufferInfo:PTR CONSOLE_SCREEN_BUFFER_INFO

其中CONSOLE_SCREEN_BUFFER_INFO结构体定义内容为:

CONSOLE_SCREEN_BUFFER_INFO STRUCT
    dwSize COORD <>
    dwCursorPosition COORD <>
    wAttributes WORD ?
    srWindow SMALL_RECT <>
    dwMaximumWindowSize COORD <>
CONSOLE_SCREEN_BUFFER_INFO ENDS

示例:

.data
    consoleInfo CONSOLE_SCREEN_BUFFER_INFO <>
    outHandle HANDLE ?
.code
    INVOKE GetConsoleScreenBufferInfo, outHandle, ADDR consoleInfo
CONSOLE_SCREEN_BUFFER_INFO structure.png
SetConsoleWindowInfo PROTO,
    hConsoleOutput:HANDLE,          ; screen buffer handle
    bAbsolute:DWORD,                ; coordinate type
    lpConsoleWindow:PTR SMALL_RECT  ; ptr to window rectangle

bAbsolute指示如何使用lpConsoleWindow指向的结构中的坐标。 如果bAbsolute为true,则坐标指定控制台窗口的新左上角和右下角。 如果bAbsolute为false,则坐标将添加到当前窗口坐标。

; 滚动控制台窗口

INCLUDE Irvine32.inc

.data
    message BYTE ": This line of text was written "
            BYTE "to the screen buffer",0dh,0ah
    messageSize DWORD ($-message)
    outHandle HANDLE 0                  ; standard output handle
    bytesWritten DWORD ?                ; number of bytes written
    lineNum DWORD 0
    windowRect SMALL_RECT <0,0,60,11>   ; left,top,right,bottom

.code
main PROC
    INVOKE GetStdHandle, STD_OUTPUT_HANDLE
    mov outHandle,eax
    .REPEAT
        mov eax,lineNum
        call WriteDec                   ; display each line number
        INVOKE WriteConsole, outHandle, ; console output handle
            ADDR message,               ; string pointer
            messageSize,                ; string length
            ADDR bytesWritten,          ; returns num bytes written
            0                           ; not used
        inc lineNum                     ; next line number
    .UNTIL lineNum > 50
    ; Resize and reposition the console window relative to the
    ; screen buffer.
    INVOKE SetConsoleWindowInfo,
        outHandle,
        TRUE,
        ADDR windowRect                 ; window rectangle
    call Readchar                       ; wait for a key
    call Clrscr                         ; clear the screen buffer
    call Readchar                       ; wait for a second key
    INVOKE ExitProcess,0
main ENDP
END main
main ENDP
END
SetConsoleScreenBufferSize PROTO,
    hConsoleOutput:HANDLE,  ; handle to screen buffer
    dwSize:COORD            ; new screen buffer size
8). 控制光标

Win32 API提供了设置光标大小,可见性和屏幕位置的功能。 与这些函数相关的重要数据结构是CONSOLE_CURSOR_INFO,其中包含有关控制台光标大小和可见性的信息。

CONSOLE_CURSOR_INFO STRUCT
    dwSize DWORD ?
    bVisible DWORD ?
CONSOLE_CURSOR_INFO ENDS

dwSize是光标填充的字符单元格的百分比(1到100)。 如果光标可见,则bVisible等于TRUE(1)。

GetConsoleCursorInfo PROTO,
    hConsoleOutput:HANDLE,
    lpConsoleCursorInfo:PTR CONSOLE_CURSOR_INFO
SetConsoleCursorInfo PROTO,
    hConsoleOutput:HANDLE,
    lpConsoleCursorInfo:PTR CONSOLE_CURSOR_INFO
SetConsoleCursorPosition PROTO,
    hConsoleOutput:DWORD,   ; input mode handle
    dwCursorPosition:COORD  ; screen X,Y coordinates
9). 控制字体颜色
SetConsoleTextAttribute PROTO,
    hConsoleOutput:HANDLE,  ; console output handle
    wAttributes:WORD        ; color attribute
WriteConsoleOutputAttribute PROTO,
    hConsoleOutput:DWORD,       ; output handle
    lpAttribute:PTR WORD,       ; write attributes
    nLength:DWORD,              ; number of cells
    dwWriteCoord:COORD,         ; first cell coordinates
    lpNumberOfAttrsWritten:PTR DWORD ; output count

lpAttribute指向一个属性数组,其中每个属性的低位字节包含颜色
nLength是数组的长度
dwWriteCoord是接收属性的起始屏幕单元格
lpNumberOfAttrsWritten指向一个变量,该变量将保存写入的单元格数

; 更改文本颜色

INCLUDE Irvine32.inc
.data
    outHandle HANDLE ?
    cellsWritten DWORD ?
    xyPos COORD <10,2>
    ; Array of character codes:
    buffer  BYTE 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
            BYTE 16,17,18,19,20
    BufSize DWORD ($-buffer)
    ; Array of attributes:
    attributes  WORD 0Fh,0Eh,0Dh,0Ch,0Bh,0Ah,9,8,7,6
                WORD 5,4,3,2,1,0F0h,0E0h,0D0h,0C0h,0B0h
.code
main PROC
    ; Get the Console standard output handle:
    INVOKE GetStdHandle,STD_OUTPUT_HANDLE
    mov outHandle,eax
    ; Set the colors of adjacent cells:
    INVOKE WriteConsoleOutputAttribute,
        outHandle, ADDR attributes,
        BufSize, xyPos, ADDR cellsWritten
    ; Write character codes 1 through 20:
    INVOKE WriteConsoleOutputCharacter,
         outHandle, ADDR buffer, BufSize,
         xyPos, ADDR cellsWritten
    INVOKE ExitProcess,0 ; end program
main ENDP
END
效果.png
10). 时间和日期函数

Win32 API提供了相当多的时间和日期函数选择。

Win32 DateTime Functions.png
SYSTEMTIME STRUCT
    wYear WORD ?        ; year (4 digits)
    wMonth WORD ?       ; month (1-12)
    wDayOfWeek WORD ?   ; day of week (0-6)
    wDay WORD ?         ; day (1-31)
    wHour WORD ?        ; hours (0-23)
    wMinute WORD ?      ; minutes (0-59)
    wSecond WORD ?      ; seconds (0-59)
    wMilliseconds WORD ?; milliseconds (0-999)
SYSTEMTIME ENDS

wDayOfWeek 里Sunday为0,然后递加。

GetLocalTime PROTO,
    lpSystemTime:PTR SYSTEMTIME

SetLocalTime PROTO,
    lpSystemTime:PTR SYSTEMTIME

示例:

.data
    sysTime SYSTEMTIME <>
.code
    INVOKE GetLocalTime, ADDR sysTime
GetTickCount PROTO ; return value in EAX

示例: 计算经过时间

; 计算经过时间

INCLUDE Irvine32.inc
INCLUDE macros.inc

.data
    startTime DWORD ?

.code
main PROC
    INVOKE GetTickCount     ; get starting tick count
    mov startTime,eax       ; save it
    ; Create a useless calculation loop.
    mov ecx,10000100h
L1: 
    imul ebx
    imul ebx
    imul ebx
    loop L1
    INVOKE GetTickCount     ; get new tick count
    cmp eax,startTime       ; lower than starting one?
    jb error                ; it wrapped around
    sub eax,startTime       ; get elapsed milliseconds
    call WriteDec           ; display it
    mWrite <" milliseconds have elapsed",0dh,0ah>
    jmp quit
error:
    mWrite "Error: GetTickCount invalid--system has"
    mWrite <"been active for more than 49.7 days",0dh,0ah>
quit:
    call WaitMsg
    call Crlf
    exit
main ENDP
END main
Sleep PROTO,
    dwMilliseconds:DWORD
;--------------------------------------------------
GetDateTime PROC,
    pStartTime:PTR QWORD
    LOCAL sysTime:SYSTEMTIME, flTime:FILETIME
;
; Gets and saves the current local date/time as a
; 64-bit integer (in the Win32 FILETIME format).
;--------------------------------------------------
; Get the system local time
    INVOKE GetLocalTime,
        ADDR sysTime
    ; Convert the SYSTEMTIME to FILETIME
    INVOKE SystemTimeToFileTime,
        ADDR sysTime,
        ADDR flTime
    ; Copy the FILETIME to a 64-bit integer
    mov esi,pStartTime
    mov eax,flTime.loDateTime
    mov DWORD PTR [esi],eax
    mov eax,flTime.hiDateTime
    mov DWORD PTR [esi+4],eax
    ret
GetDateTime ENDP

2. Windows 图形应用

Win32 图形应用需要的文件如下图, 使用/SUBSYSTEM:WINDOWS替换/SUBSYSTEM:CONSOLE(修改方法:在解决方法管理器中,项目名上右键 -> 属性 -> 配置属性 -> 链接器 -> 系统 -> 子系统,属性 -> 配置属性 -> 链接器 -> 高级 -> 入口点main@0 改为WinMain@0)

Files Required When Building the WinApp Program.png
1). 必要的结构
POINT STRUCT
    ptX DWORD ?
    ptY DWORD ?
POINT ENDS
RECT STRUCT
    left DWORD ?
    top DWORD ?
    right DWORD ?
    bottom DWORD ?
RECT ENDS
MSGStruct STRUCT
    msgWnd DWORD ?
    msgMessage DWORD ?
    msgWparam DWORD ?
    msgLparam DWORD ?
    msgTime DWORD ?
    msgPt POINT <>
MSGStruct ENDS
WNDCLASS STRUC
    style DWORD ?       ; window style options
    lpfnWndProc DWORD ? ; pointer to WinProc function
    cbClsExtra DWORD ?  ; shared memory
    cbWndExtra DWORD ?  ; number of extra bytes
    hInstance DWORD ?   ; handle to current program
    hIcon DWORD ?       ; handle to icon
    hCursor DWORD ?     ; handle to cursor
    hbrBackground DWORD ?   ; handle to background brush
    lpszMenuName DWORD ?    ; pointer to menu name
    lpszClassName DWORD ?   ; pointer to WinClass name
WNDCLASS ENDS
2). WinMain 程序

每个Windows应用程序都需要一个启动过程,通常名为WinMain,它负责以下任务:
•获取当前程序的句柄。
•加载程序的图标和鼠标光标。
•注册程序的主窗口类,并确定将处理窗口事件消息的过程。
•创建主窗口。
•显示和更新主窗口。
•开始接收和发送消息的循环。 循环继续,直到用户关闭应用程序窗口。

3). WinProc 程序

WinProc过程接收并处理与窗口有关的所有事件消息。 大多数事件由用户通过单击并拖动鼠标,按键盘键等启动。

WinProc PROC,
    hWnd:DWORD,     ; handle to the window
    localMsg:DWORD, ; message ID
    wParam:DWORD,   ; parameter 1 (varies)
    lParam:DWORD    ; parameter 2 (varies)

WinProc 中的三种特殊消息:
•WM_LBUTTONDOWN,用户按下鼠标左键时生成
•WM_CREATE,表示刚刚创建了主窗口
•WM_CLOSE,表示应用程序的主窗口即将关闭

4). ErrorHandler 程序

如果系统在注册和创建程序主窗口期间报告错误,则会调用ErrorHandler过程(可选)。

ErrorHandler过程有几个重要的任务要执行:
•调用GetLastError以检索系统错误号。
•调用FormatMessage以检索适当的系统格式错误消息字符串。
•调用MessageBox以显示包含错误消息字符串的弹出消息框。
•调用LocalFree以释放错误消息字符串使用的内存。

5). 程序示例
; Windows Application (WinApp.asm)
; This program displays a resizable application window and
; several popup message boxes. Special thanks to Tom Joyce
; for the first version of this program.

INCLUDE Irvine32.inc
INCLUDE GraphWin.inc

;==================== DATA =======================
.data
    AppLoadMsgTitle BYTE "Application Loaded",0
    AppLoadMsgText BYTE "This window displays when the WM_CREATE "
        BYTE "message is received",0
    PopupTitle BYTE "Popup Window",0
    PopupText BYTE "This window was activated by a "
        BYTE "WM_LBUTTONDOWN message",0
    GreetTitle BYTE "Main Window Active",0
    GreetText BYTE "This window is shown immediately after "
        BYTE "CreateWindow and UpdateWindow are called.",0
    CloseMsg BYTE "WM_CLOSE message received",0
    ErrorTitle BYTE "Error",0
    WindowName BYTE "ASM Windows App",0
    className BYTE "ASMWin",0

    ; Define the Application's Window class structure.
    MainWin WNDCLASS <NULL,WinProc,NULL,NULL,NULL,NULL,NULL, \
    COLOR_WINDOW,NULL,className>
    msg MSGStruct <>
    winRect RECT <>
    hMainWnd DWORD ?
    hInstance DWORD ?

;=================== CODE =========================
.code
WinMain PROC
    ; Get a handle to the current process.
    INVOKE GetModuleHandle, NULL
    mov hInstance, eax
    mov MainWin.hInstance, eax
    ; Load the program's icon and cursor.
    INVOKE LoadIcon, NULL, IDI_APPLICATION
    mov MainWin.hIcon, eax
    INVOKE LoadCursor, NULL, IDC_ARROW
    mov MainWin.hCursor, eax
    ; Register the window class.
    INVOKE RegisterClass, ADDR MainWin
    .IF eax == 0
        call ErrorHandler
        jmp Exit_Program
    .ENDIF
    ; Create the application's main window.
    INVOKE CreateWindowEx, 0, ADDR className,
        ADDR WindowName,MAIN_WINDOW_STYLE,
        CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
        CW_USEDEFAULT,NULL,NULL,hInstance,NULL
    ; If CreateWindowEx failed, display a message and exit.
    .IF eax == 0
        call ErrorHandler
        jmp Exit_Program
    .ENDIF
    ; Save the window handle, show and draw the window.
    mov hMainWnd,eax
    INVOKE ShowWindow, hMainWnd, SW_SHOW
    INVOKE UpdateWindow, hMainWnd
    ; Display a greeting message.
    INVOKE MessageBox, hMainWnd, ADDR GreetText,
        ADDR GreetTitle, MB_OK
    ; Begin the program's continuous message-handling loop.
Message_Loop:
    ; Get next message from the queue.
    INVOKE GetMessage, ADDR msg, NULL,NULL,NULL
    ; Quit if no more messages.
    .IF eax == 0
        jmp Exit_Program
    .ENDIF
    ; Relay the message to the program's WinProc.
    INVOKE DispatchMessage, ADDR msg
    jmp Message_Loop
Exit_Program:
    INVOKE ExitProcess,0
WinMain ENDP

;-----------------------------------------------------
WinProc PROC,
hWnd:DWORD, localMsg:DWORD, wParam:DWORD, lParam:DWORD
;
; The application's message handler, which handles
; application-specific messages. All other messages
; are forwarded to the default Windows message
; handler.
;-----------------------------------------------------
    mov eax, localMsg
    .IF eax == WM_LBUTTONDOWN ; mouse button?
        INVOKE MessageBox, hWnd, ADDR PopupText,
        ADDR PopupTitle, MB_OK
        jmp WinProcExit
    .ELSEIF eax == WM_CREATE ; create window?
        INVOKE MessageBox, hWnd, ADDR AppLoadMsgText,
        ADDR AppLoadMsgTitle, MB_OK
        jmp WinProcExit
    .ELSEIF eax == WM_CLOSE ; close window?
        INVOKE MessageBox, hWnd, ADDR CloseMsg,
        ADDR WindowName, MB_OK
        INVOKE PostQuitMessage,0
        jmp WinProcExit
    .ELSE ; other message?
        INVOKE DefWindowProc, hWnd, localMsg, wParam, lParam
        jmp WinProcExit
    .ENDIF
WinProcExit:
    ret
WinProc ENDP

;---------------------------------------------------
ErrorHandler PROC
; Display the appropriate system error message.
;---------------------------------------------------
.data
    pErrorMsg DWORD ? ; ptr to error message
    messageID DWORD ?
.code
    INVOKE GetLastError ; Returns message ID in EAX
    mov messageID,eax
    ; Get the corresponding message string.
    INVOKE FormatMessage, FORMAT_MESSAGE_ALLOCATE_BUFFER + \
        FORMAT_MESSAGE_FROM_SYSTEM,NULL,messageID,NULL,
        ADDR pErrorMsg,NULL,NULL
    ; Display the error message.
    INVOKE MessageBox,NULL, pErrorMsg, ADDR ErrorTitle,
        MB_ICONERROR+MB_OK
    ; Free the error message string.
    INVOKE LocalFree, pErrorMsg
    ret
ErrorHandler ENDP
END WinMain

3. 动态申请内存

动态内存分配(也称为堆分配)是一种编程语言用于在创建对象,数组和其他结构时保留内存的技术。
C,C ++和Java具有内置的运行时堆管理器,用于处理存储分配和释放的编程请求。 堆启动管理器通常在程序启动时从操作系统分配大块内存。 他们创建了一个指向存储块的免费指针列表。 当收到分配请求时,堆管理器将适当大小的内存块标记为保留,并返回指向该块的指针。 稍后,当收到对同一块的删除请求时,堆会释放该块,并将其返回到空闲列表。 每次收到新的分配请求时,堆管理器都会扫描空闲列表,查找足够大的第一个可用块以授予请求。

Heap-Related Functions.png
GetProcessHeap PROTO

示例:

.data
    hHeap HANDLE ?
.code
    INVOKE GetProcessHeap
    .IF eax == NULL ; cannot get handle
        jmp quit
    .ELSE
        mov hHeap,eax ; handle is OK
    .ENDIF
HeapCreate PROTO,
    flOptions:DWORD, ; heap allocation options
    dwInitialSize:DWORD, ; initial heap size, in bytes
    dwMaximumSize:DWORD ; maximum heap size, in bytes

flOptions设置为NULL
dwInitialSize设置为初始堆大小(以字节为单位)。 该值向上舍入到下一页边界
HeapAlloc调用时超过初始堆大小时,它将增大到您在dwMaximumSize参数中指定的值(向上舍入到下一页边界)。 调用它之后,EAX中的空返回值表示未创建堆

示例:

HEAP_START = 2000000 ; 2 MB
HEAP_MAX = 400000000 ; 400 MB
.data
    hHeap HANDLE ? ; handle to heap
.code
    INVOKE HeapCreate, 0, HEAP_START, HEAP_MAX
    .IF eax == NULL ; heap not created
        call WriteWindowsMsg ; show error message
        jmp quit
    .ELSE
        mov hHeap,eax ; handle is OK
    .ENDIF
HeapDestroy PROTO,
    hHeap:DWORD ; heap handle

示例:

.data
    hHeap HANDLE ? ; handle to heap
.code
    INVOKE HeapDestroy, hHeap
    .IF eax == NULL
        call WriteWindowsMsg ; show error message
    .ENDIF
HeapAlloc PROTO,
    hHeap:HANDLE,   ; handle to existing heap block
    dwFlags:DWORD,  ; heap allocation control flags
    dwBytes:DWORD   ; number of bytes to allocate

hHeap一个由GetProcessHeap或HeapCreate初始化的堆的32位句柄。
dwFlags包含一个或多个标志值的双字。 您可以选择将其设置为HEAP_ZERO_MEMORY,它将内存块设置为全零。
dwBytes,一个双字,表示堆分配的大小,以字节为单位

.data
    hHeap HANDLE ? ; heap handle
    pArray DWORD ? ; pointer to array
.code
    INVOKE HeapAlloc, hHeap, HEAP_ZERO_MEMORY, 1000
    .IF eax == NULL
        mWrite "HeapAlloc failed"
        jmp quit
    .ELSE
        mov pArray,eax
    .ENDIF
HeapFree PROTO,
    hHeap:HANDLE,
    dwFlags:DWORD,
    lpMem:DWORD

示例:

INVOKE HeapFree, hHeap, 0, pArray
INVOKE HeapCreate, 0,HEAP_START, HEAP_MAX
.IF eax == NULL ; failed?
    call WriteWindowsMsg ; show error message
.ELSE
    mov hHeap,eax ; success
.ENDIF
1). 堆测试程序
; 堆测试
; 程序动态申请1000个字节的内存
INCLUDE Irvine32.inc

.data
    ARRAY_SIZE = 1000
    FILL_VAL EQU 0FFh
    hHeap HANDLE ?      ; 进程堆句柄
    pArray DWORD ?      ; 指向内存块
    newHeap DWORD ?     ; 新堆的句柄
    str1 BYTE "Heap size is: ",0
.code
main PROC
    INVOKE GetProcessHeap   ; 获取堆句柄
    .IF eax == NULL         ; 如果获取失败,则显示异常信息
        call WriteWindowsMsg
        jmp quit
    .ELSE
        mov hHeap,eax       ; 获取成功
    .ENDIF
    call allocate_array
    jnc arrayOk             ; 失败,设置CF=1
    call WriteWindowsMsg
    call Crlf
    jmp quit
arrayOk:                    ; 字节填充
    call fill_array
    call display_array
    call Crlf
    ; free the array
    INVOKE HeapFree, hHeap, 0, pArray
quit:
    exit
main ENDP

;--------------------------------------------------------
allocate_array PROC USES eax
; 为数组动态申请内存空间
; Receives: EAX = handle to the program heap
; Returns: CF = 0 if the memory allocation succeeds.
;--------------------------------------------------------
    INVOKE HeapAlloc, hHeap, HEAP_ZERO_MEMORY, ARRAY_SIZE
    .IF eax == NULL
        stc             ; 返回时CF=1
    .ELSE
        mov pArray,eax  ; 保存指针
        clc             ; 返回时CF=0
    .ENDIF
    ret
allocate_array ENDP

;--------------------------------------------------------
fill_array PROC USES ecx edx esi
; 填充单个字符
; Receives: nothing
; Returns: nothing
;--------------------------------------------------------
    mov ecx,ARRAY_SIZE  ; 循环计数
    mov esi,pArray      ; 数组指针
    L1: mov BYTE PTR [esi],FILL_VAL ; 填充每个字节
    inc esi             ; 下一个位置
    loop L1
    ret
fill_array ENDP

;--------------------------------------------------------
display_array PROC USES eax ebx ecx esi
; 显示数组
; Receives: nothing
; Returns: nothing
;--------------------------------------------------------
    mov ecx,ARRAY_SIZE  ; 循环计数
    mov esi,pArray      ; 数组指针
    L1: mov al,[esi]    ; 获取一个字节
    mov ebx,TYPE BYTE
    call WriteHexB      ; 显示
    inc esi             ; 下一个位置
    loop L1
    ret
display_array ENDP
END main

4. x86 内存管理

1). 线性地址
Converting a logical address into a linear address.png Indexing into a local descriptor table.png
2). 页面翻译

启用分页时,处理器必须将32位线性地址转换为32位物理地址。 该过程中使用了三种结构:
•页面目录:最多1024个32位页面目录条目的数组。
•页表:最多1024个32位页表条目的数组。
•页面:4 KB或4 MB的地址空间。

Translating linear address to physical address.png
上一篇下一篇

猜你喜欢

热点阅读