美文网首页汇编(MOV,SUB,PUSH,POP,...)
汇编开发(九):MS-Windows 程序

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

作者: _凌浩雨 | 来源:发表于2019-02-24 17:30 被阅读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为消息框的内容和行为。

    • 内容和行为
      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 <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
      如果您满足于使用当前程序拥有的默认堆,则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 程序

        本文链接:https://www.haomeiwen.com/subject/uymuyqtx.html