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

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为消息框的内容和行为。

  • 内容和行为
    uType参数包含一个位映射整数,它包含三种类型的选项:要显示的按钮,图标和默认按钮选项。 其中按钮有7个常量值:MB_OK, MB_OKCANCEL, MB_YESNO, MB_YESNOCANCEL, MB_RETRYCANCEL, MB_ABORTRETRYIGNORE, MB_CANCELTRYCONTINUE

  • 默认的按钮
    当按下Enter键的时候,可以自动选择默认的按钮。

  • 图标
    四种图片可供选择。

    • 停止: MB_ICONSTOP, MB_ICONHAND, or MB_ICONERROR
    • 问号标记(?) : MB_ICONQUESTION
    • 消息标记(i): MB_ICONINFORMATION, MB_ICONASTERISK
    • 警告标记(!): MB_ICONEXCLAMATION, MB_ICONWARNING
  • 返回值
    如果MessageBoxA失败了,则返回0,否则返回用户点击的按钮对应的整型。它们是IDABORT, IDCANCEL,IDCONTINUE, IDIGNORE, IDNO, IDOK, IDRETRY, IDTRYAGAIN, and IDYES。

示例:

; 消息框

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
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
  • 检查错误
    如果Windows API 函数返回一个错误值,我们可以调用GetLastError 函数来获取更多的错误信息。
.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
    显示一个最近异常信息的字符串。
;----------------------------------------------------
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
  • 单字符输入
    控制台模式下的单字符输入有点棘手。 MS-Windows为当前安装的键盘提供设备驱动程序。 按下某个键时,会将8位扫描码传输到计算机的键盘端口。 当释放密钥时,发送第二扫描代码。 MSWindows使用设备驱动程序将扫描代码转换为16位虚拟密钥代码,这是一种由MS-Windows定义的与设备无关的值,用于标识密钥的用途。 MS-Windows创建一条消息,其中包含扫描代码,虚拟密钥代码和其他相关信息。
    在Irvine32库中相关函数有:
    • ReadChar: 从键盘读取一个字符的ASCII码,返回这个字符到AL中。
    • ReadKey: 执行无等待键盘检查。如果控制台输入缓冲区中没有等待键,则设置Zero标志。 如果找到密钥,则清零零标志,AL包含零或ASCII代码。 EAX和EDX的上半部分被覆盖。
Keyboard Control Key State Values.png
  • ReadKey测试程序
; 测试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). 控制台输出
  • 数据结构
    一些Win32控制台功能使用预定义的数据结构,包括COORD和SMALL_RECT。

  • WriteConsole 函数
    WriteConsole函数将字符串写入当前光标位置的控制台窗口,并将光标留在最后写入的字符之后。

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 函数
    WriteConsoleOutputCharacter函数将字符数组复制到控制台屏幕缓冲区的连续单元格,从指定位置开始。
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 函数

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 函数
    CloseHandle函数关闭一个打开的文件句柄。
CloseHandle PROTO,
    hObject:HANDLE ; handle to object
  • ReadFile 函数
    ReadFile函数从一个文件中读取文本。
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 函数
    WriteFile函数使用输出句柄将数据写入文件。 句柄可以是屏幕缓冲区句柄,也可以是分配给文本文件的句柄。 该函数开始在文件内部位置指针指示的位置将数据写入文件。 写操作完成后,文件的位置指针由实际写入的字节数调整。
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 函数
    SetFilePointer函数移动打开文件的位置指针.
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移动屏幕缓冲区中的部分或全部文本,这会影响控制台窗口中显示的文本

  • SetConsoleTitle 函数
    SetConsoleTitle函数可让我们改变控制他窗口的标题。
.data
    titleStr BYTE "Console title",0
.code
    INVOKE SetConsoleTitle, ADDR titleStr
  • GetConsoleScreenBufferInfo 函数
    GetConsoleScreenBufferInfo函数返回控制台窗口的当前状态信息。 它有量参数:一个是控制台窗口句柄,另一个是指向由函数填充的结构的指针。
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 函数
    SetConsoleWindowInfo函数允许您设置控制台窗口相对于其屏幕缓冲区的大小和位置。
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 函数
    SetConsoleScreenBufferSize函数允许您将屏幕缓冲区大小设置为X列Y行。
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 函数
    GetConsoleCursorInfo 函数返回光标的可见性和大小。
GetConsoleCursorInfo PROTO,
    hConsoleOutput:HANDLE,
    lpConsoleCursorInfo:PTR CONSOLE_CURSOR_INFO
  • SetConsoleCursorInfo 函数
    SetConsoleCursorInfo 函数设置光标的可见与大小。
SetConsoleCursorInfo PROTO,
    hConsoleOutput:HANDLE,
    lpConsoleCursorInfo:PTR CONSOLE_CURSOR_INFO
  • SetConsoleCursorPosition 函数
    SetConsoleCursorPosition 函数设置光标的位置。
SetConsoleCursorPosition PROTO,
    hConsoleOutput:DWORD,   ; input mode handle
    dwCursorPosition:COORD  ; screen X,Y coordinates
9). 控制字体颜色
  • SetConsoleTextAttribute 函数
    SetConsoleTextAttribute 函数允许您将所有后续文本输出的前景色和背景色设置到控制台窗口.
SetConsoleTextAttribute PROTO,
    hConsoleOutput:HANDLE,  ; console output handle
    wAttributes:WORD        ; color attribute
  • WriteConsoleOutputAttribute 函数
    WriteConsoleOutputAttribute 函数从指定位置开始,将一组属性值复制到控制台屏幕缓冲区的连续单元格。
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 结构体
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 和 SetLocalTime 函数
    GetLocalTime函数根据系统时钟返回日期和当前时间。SetLocalTime函数设置系统的本地日期和时间。
GetLocalTime PROTO,
    lpSystemTime:PTR SYSTEMTIME

SetLocalTime PROTO,
    lpSystemTime:PTR SYSTEMTIME

示例:

.data
    sysTime SYSTEMTIME <>
.code
    INVOKE GetLocalTime, ADDR sysTime
  • GetTickCount 函数
    GetTickCount函数返回自系统启动以来经过的毫秒数。
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 函数
    程序有时需要暂停或延迟很短的时间。 虽然可以构建一个计算循环或繁忙循环来保持处理器忙,但循环的执行时间会因处理器而异。 此外,繁忙的循环将不必要地占用处理器,从而减慢同时执行的其他程序的速度。
Sleep PROTO,
    dwMilliseconds:DWORD
  • GetDateTime 程序
    Irvine32库中的GetDateTime过程返回自1601年1月1日以来经过的100纳秒时间间隔的数量。
;--------------------------------------------------
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 结构体
    POINT结构指定屏幕上某点的X和Y坐标,以像素为单位。
POINT STRUCT
    ptX DWORD ?
    ptY DWORD ?
POINT ENDS
  • RECT 结构体
    RECT结构定义矩形的边界。 左侧成员包含矩形左侧的X坐标。 顶部成员包含矩形顶部的Y坐标。
RECT STRUCT
    left DWORD ?
    top DWORD ?
    right DWORD ?
    bottom DWORD ?
RECT ENDS
  • MSGStruct 结构体
    MSGStruct结构定义了MS-Windows消息所需的数据。
MSGStruct STRUCT
    msgWnd DWORD ?
    msgMessage DWORD ?
    msgWparam DWORD ?
    msgLparam DWORD ?
    msgTime DWORD ?
    msgPt POINT <>
MSGStruct ENDS
  • WNDCLASS结构体
    WNDCLASS结构定义了一个窗口类。 程序中的每个窗口都必须属于一个类,每个程序必须为其主窗口定义一个窗口类。
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 
    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
    如果您满足于使用当前程序拥有的默认堆,则GetProcessHeap就足够了。
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
    创建私有堆空间
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
    HeapDestroy 函数销毁已存在的私有堆空间
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
    HeapAlloc 函数在已存在的堆中申请内存块
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
    HeapFree 函数在已经存在的内存块中释放私有空间。
HeapFree PROTO,
    hHeap:HANDLE,
    dwFlags:DWORD,
    lpMem:DWORD

示例:

INVOKE HeapFree, hHeap, 0, pArray
  • Error Handling
    如果在调用HeapCreate,HeapDestroy或GetProcessHeap时遇到错误,可以通过调用GetLastError API函数来获取详细信息。
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). 线性地址
  • 将逻辑地址转换为线性地址
    多任务操作系统允许多个程序(任务)同时在内存中运行。 每个程序都有自己独特的数据区域。 假设三个程序每个都有一个偏移量为200h的变量; 三个变量如何在不共享的情况下彼此分离? 答案是x86处理器使用一步或两步过程将每个变量的偏移量转换为唯一的内存位置.
    第一步将段值与变量的偏移量组合在一起以创建线性地址。 该线性地址可以是变量的物理地址。 但是,诸如MS-Windows和Linux之类的操作系统采用称为分页的功能,以允许程序使用比计算机中物理可用的线性内存更多的线性内存。 他们必须使用称为页面转换的第二步将线性地址转换为物理地址。
Converting a logical address into a linear address.png
  • 分页
    分页是x86处理器的一个重要特性,它使计算机可以运行一些本来不适合内存的程序组合。 处理器通过最初仅将部分程序加载到内存中,同时将剩余部分保留在磁盘上来完成此操作。 程序使用的内存分为称为页面的小单元,每个单元通常为4 KB。 当每个程序运行时,处理器选择性地从内存中卸载非活动页面并加载其他立即需要的页面。

  • 描述表
    段描述符可以在两种类型的表中找到:全局描述符表和本地描述符表。
    全局描述符表——当操作系统在启动期间将处理器切换到保护模式时,会创建单个GDT。 其基址保存在GDTR(全局描述符表寄存器)中。
    本地描述符表——在多任务操作系统中,通常为每个任务或程序分配其自己的段描述符表,称为LDT。 LDTR寄存器包含程序LDT的地址。

  • 段描述符详细信息
    除了段的基址之外,段描述符还包含指定段限制和段类型的位映射字段。

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

你可能感兴趣的:(汇编开发(九):MS-Windows 程序)