匯編開發(fā)(九):MS-Windows 程序

1. Win32 控制臺(tái)程序

1). 顯示一個(gè)消息框
MessageBoxA PROTO,
    hWnd:DWORD, ; handle to window (can be null)
    lpText:PTR BYTE, ; 消息框內(nèi)部
    lpCaption:PTR BYTE, ; 消息框標(biāo)題
    uType:DWORD ; 內(nèi)容和行為

hWnd在控制臺(tái)應(yīng)用中可以設(shè)置為NULL
lpText為一個(gè)以空值終止的字符串指針
lpCaotion為一個(gè)以空值終止的對(duì)話框標(biāo)題字符串指針
uType為消息框的內(nèi)容和行為顽腾。

  • 內(nèi)容和行為
    uType參數(shù)包含一個(gè)位映射整數(shù)崔拥,它包含三種類型的選項(xiàng):要顯示的按鈕,圖標(biāo)和默認(rèn)按鈕選項(xiàng)漾月。 其中按鈕有7個(gè)常量值:MB_OK, MB_OKCANCEL, MB_YESNO, MB_YESNOCANCEL, MB_RETRYCANCEL, MB_ABORTRETRYIGNORE, MB_CANCELTRYCONTINUE饲帅。

  • 默認(rèn)的按鈕
    當(dāng)按下Enter鍵的時(shí)候矛绘,可以自動(dòng)選擇默認(rèn)的按鈕。

  • 圖標(biāo)
    四種圖片可供選擇辱匿。

    • 停止: MB_ICONSTOP, MB_ICONHAND, or MB_ICONERROR
    • 問號(hào)標(biāo)記(?) : MB_ICONQUESTION
    • 消息標(biāo)記(i): MB_ICONINFORMATION, MB_ICONASTERISK
    • 警告標(biāo)記(!): MB_ICONEXCLAMATION, MB_ICONWARNING
  • 返回值
    如果MessageBoxA失敗了键痛,則返回0,否則返回用戶點(diǎn)擊的按鈕對(duì)應(yīng)的整型匾七。它們是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
    ; 顯示一個(gè)帶圖標(biāo)的OK按鈕
    INVOKE MessageBox, NULL, ADDR warningMsg, ADDR captionW, MB_OK + MB_ICONEXCLAMATION

    ; 顯示一個(gè)帶問號(hào)的圖標(biāo)和Yes/No按鈕
    INVOKE MessageBox, NULL, ADDR questionMsg, ADDR captionQ, MB_YESNO + MB_ICONQUESTION
    
    ; 顯示一個(gè)消息圖標(biāo)和Yes/No/Cancel按鈕
    INVOKE MessageBox, NULL, ADDR infoMsg, ADDR captionC, MB_YESNOCANCEL + MB_ICONINFORMATION + MB_DEFBUTTON2

    ; 顯示一個(gè)停止圖標(biāo)和一個(gè)OK按鈕
    INVOKE MessageBox, NULL, ADDR haltMsg, ADDR captionH, MB_OK + MB_ICONSTOP

    exit
main ENDP
END
效果.gif
2). 控制臺(tái)輸入
  • ReadConsole
ReadConsole PROTO,
    hConsoleInput:HANDLE,           ; input handle
    lpBuffer:PTR BYTE,              ; 緩沖指針
    nNumberOfCharsToRead:DWORD,     ; 要讀取的字符數(shù)
    lpNumberOfCharsRead:PTR DWORD,  ; 要讀取的字符指針
    lpReserved:DWORD                ; (不使用)

hConsoleInput一個(gè)有效的控制臺(tái)輸入句柄,使用GetStdHandle函數(shù)獲取
lpBuffer字符數(shù)組首地址
nNumberOfCharsToRead要讀取的字符數(shù)
lpNumberOfCharsRead要讀取的字符指針
lpReserved保留參數(shù)

示例:

; 從控制臺(tái)讀取數(shù)據(jù)

INCLUDE Irvine32.inc

BufSize = 80

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

.code 
main PROC
    ; 獲取標(biāo)準(zhǔn)輸入句柄
    INVOKE GetStdHandle, STD_INPUT_HANDLE
    mov stdInHandle, eax

    ; 等待用戶輸入
    INVOKE ReadConsole, stdInHandle, ADDR buffer, BufSize, ADDR bytesRead, 0
    ; 顯示緩沖內(nèi)容
    mov esi, OFFSET buffer
    mov ecx, bytesRead
    mov ebx, TYPE buffer
    call DumpMem

    call WaitMsg
    call Crlf
    exit
main ENDP
END
效果.png
  • 檢查錯(cuò)誤
    如果Windows API 函數(shù)返回一個(gè)錯(cuò)誤值昨忆,我們可以調(diào)用GetLastError 函數(shù)來獲取更多的錯(cuò)誤信息丁频。
.data
    messageId DWORD ?
.code
    call GetLastError
    mov messageId,eax

獲取到錯(cuò)誤碼之后,我們可以調(diào)用FormatMessage 函數(shù)獲取具體信息

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是輸出參數(shù)邑贴,其余均為輸入?yún)?shù)席里。

dwFlags是一個(gè)整型的格式選項(xiàng)。
lpSource是一個(gè)消息的地址指針拢驾。
dwMsgID是通過GetLastError獲取到的雙字整型值奖磁。
dwLanguageID是語言標(biāo)識(shí)。如果設(shè)置為0繁疤,則消息將是用戶默認(rèn)的語言
lpBuffer是一個(gè)以空值終止的字符串指針咖为。
nSize是可以使用一個(gè)特殊的緩沖來獲取消息。
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
    顯示一個(gè)最近異常信息的字符串躁染。
;----------------------------------------------------
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
  • 單字符輸入
    控制臺(tái)模式下的單字符輸入有點(diǎn)棘手。 MS-Windows為當(dāng)前安裝的鍵盤提供設(shè)備驅(qū)動(dòng)程序架忌。 按下某個(gè)鍵時(shí)褐啡,會(huì)將8位掃描碼傳輸?shù)接?jì)算機(jī)的鍵盤端口。 當(dāng)釋放密鑰時(shí)鳖昌,發(fā)送第二掃描代碼。 MSWindows使用設(shè)備驅(qū)動(dòng)程序?qū)呙璐a轉(zhuǎn)換為16位虛擬密鑰代碼低飒,這是一種由MS-Windows定義的與設(shè)備無關(guān)的值许昨,用于標(biāo)識(shí)密鑰的用途。 MS-Windows創(chuàng)建一條消息褥赊,其中包含掃描代碼糕档,虛擬密鑰代碼和其他相關(guān)信息。
    在Irvine32庫(kù)中相關(guān)函數(shù)有:
    • ReadChar: 從鍵盤讀取一個(gè)字符的ASCII碼,返回這個(gè)字符到AL中速那。
    • ReadKey: 執(zhí)行無等待鍵盤檢查俐银。如果控制臺(tái)輸入緩沖區(qū)中沒有等待鍵,則設(shè)置Zero標(biāo)志端仰。 如果找到密鑰捶惜,則清零零標(biāo)志,AL包含零或ASCII代碼荔烧。 EAX和EDX的上半部分被覆蓋吱七。
Keyboard Control Key State Values.png
  • ReadKey測(cè)試程序
; 測(cè)試ReadKey

INCLUDE Irvine32.inc
INCLUDE Macros.inc

.code 
main PROC
L1:
    mov eax, 10             ; 延時(shí)消息
    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
  • 獲取鍵盤狀態(tài)
    可以測(cè)試各個(gè)鍵盤鍵的狀態(tài),以找出當(dāng)前按下的鍵鹤竭。
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). 控制臺(tái)輸出
  • 數(shù)據(jù)結(jié)構(gòu)
    一些Win32控制臺(tái)功能使用預(yù)定義的數(shù)據(jù)結(jié)構(gòu)踊餐,包括COORD和SMALL_RECT。

  • WriteConsole 函數(shù)
    WriteConsole函數(shù)將字符串寫入當(dāng)前光標(biāo)位置的控制臺(tái)窗口臀稚,并將光標(biāo)留在最后寫入的字符之后吝岭。

WriteConsole PROTO,
    hConsoleOutput:HANDLE,
    lpBuffer:PTR BYTE,
    nNumberOfCharsToWrite:DWORD,
    lpNumberOfCharsWritten:PTR DWORD,
    lpReserved:DWORD

hConsoleOutput控制臺(tái)輸出流句柄
lpBuffer用戶想要輸出的數(shù)組指針
nNumberOfCharsToWrite數(shù)組長(zhǎng)度
lpNumberOfCharsWritten指向一個(gè)整數(shù),該整數(shù)分配了函數(shù)返回時(shí)實(shí)際寫入的字節(jié)數(shù)吧寺。
lpReserved保留參數(shù)窜管。

示例:

; 程序調(diào)用的Win32 控制臺(tái)函數(shù): 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
    ; 獲取控制臺(tái)輸出句柄
    INVOKE GetStdHandle, STD_OUTPUT_HANDLE
    mov consoleHandle, eax

    ; 在控制臺(tái)輸出一個(gè)字符串
    INVOKE WriteConsole,
        consoleHandle,
        ADDR message,
        messageSize,
        ADDR bytesWritten,
        0
    call WaitMsg
    INVOKE ExitProcess, 0
main ENDP
END
  • WriteConsoleOutputCharacter 函數(shù)
    WriteConsoleOutputCharacter函數(shù)將字符數(shù)組復(fù)制到控制臺(tái)屏幕緩沖區(qū)的連續(xù)單元格,從指定位置開始撮执。
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 函數(shù)

CreateFile函數(shù)可以創(chuàng)建新文件或打開現(xiàn)有文件微峰。 如果成功,則返回打開文件的句柄; 否則抒钱,它返回一個(gè)名為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: 允許您指定對(duì)文件的讀訪問,寫訪問谋币,讀/寫訪問或設(shè)備查詢?cè)L問仗扬。

dwDesiredAccess Parameter Options.png

dwCreationDisposition: 指定對(duì)存在的文件執(zhí)行的操作以及文件不存在時(shí)要執(zhí)行的操作。

dwCreationDisposition Parameter Options.png

dwFlagsAndAttributes: 除了所有其他文件屬性都覆蓋FILE_ATTRIBUTE_NORMAL之外蕾额,任何屬性組合都是可接受的早芭。

Selected FlagsAndAttributes Values.png

示例:
—— 讀取一個(gè)已存在的文件

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

—— 為已存在的文件寫內(nèi)容

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

—— 創(chuàng)建一個(gè)文件,并設(shè)置普通屬性诅蝶,擦除已有的文件內(nèi)容

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

—— 如果文件不存在則創(chuàng)建新文件退个,如果存在則打開

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 函數(shù)
    CloseHandle函數(shù)關(guān)閉一個(gè)打開的文件句柄。
CloseHandle PROTO,
    hObject:HANDLE ; handle to object
  • ReadFile 函數(shù)
    ReadFile函數(shù)從一個(gè)文件中讀取文本调炬。
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是一個(gè)CreateFile函數(shù)返回的文件句柄
lpBuffer是從文件中接收到的數(shù)據(jù)指針
nNumberOfBytesToRead指定從文件中讀取的最大字節(jié)數(shù)
lpNumberOfBytesRead指向一個(gè)整數(shù)语盈,表示函數(shù)返回時(shí)實(shí)際讀取的字節(jié)數(shù)
lpOverlapped應(yīng)該設(shè)置為NULL(0)進(jìn)行同步讀取(我們使用)缰泡。 如果函數(shù)失敗刀荒,則返回值為零。

  • WriteFile 函數(shù)
    WriteFile函數(shù)使用輸出句柄將數(shù)據(jù)寫入文件。 句柄可以是屏幕緩沖區(qū)句柄缠借,也可以是分配給文本文件的句柄干毅。 該函數(shù)開始在文件內(nèi)部位置指針指示的位置將數(shù)據(jù)寫入文件。 寫操作完成后泼返,文件的位置指針由實(shí)際寫入的字節(jié)數(shù)調(diào)整硝逢。
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是要寫入文件內(nèi)容指針
nNumberOfBytesToWrite是指定寫入文件的字節(jié)數(shù)
lpNumberOfBytesWritten是指向一個(gè)整數(shù),該整數(shù)指定函數(shù)執(zhí)行后實(shí)際寫入的字節(jié)數(shù)
lpOverlapped對(duì)于同步操作符隙,lpOverlapped應(yīng)設(shè)置為NULL趴捅。 如果函數(shù)失敗,則返回值為零

  • SetFilePointer 函數(shù)
    SetFilePointer函數(shù)移動(dòng)打開文件的位置指針.
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 移動(dòng)指針的起始位置霹疫,有三個(gè)可選常量FILE_BEGIN, FILE_CURRENT, and FILE_END拱绑。

示例:

INVOKE SetFilePointer,
    fileHandle, ; file handle
    0,          ; distance low
    0,          ; distance high
    FILE_END    ; move method
5). Irvine32 庫(kù)中的文件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). 測(cè)試文件I/O程序
  • 寫文件程序
; 創(chuàng)建文件, 生成的文件在項(xiàng)目的根目錄下
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
    ; 創(chuàng)建一個(gè)新的文本文件
    mov edx, OFFSET filename
    call CreateOutputFile
    mov fileHandle, eax

    ; 檢查錯(cuò)誤
    cmp eax, INVALID_HANDLE_VALUE   ; 是否有錯(cuò)誤
    jne file_ok                     ; no: skip
    mov edx, OFFSET str1            ; 顯示錯(cuò)誤
    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           ; 計(jì)算字符串的字符數(shù)

    ; 寫入文件
    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
  • 讀文件程序
; 讀取文件內(nèi)容

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

    ; 檢查錯(cuò)誤
    cmp eax, INVALID_HANDLE_VALUE
    jne file_ok
    mWrite <"Cannot open file", 0dh, 0ah>
    jmp quit

file_ok:
    ; 讀取文件內(nèi)容到緩沖區(qū)
    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
    ; 顯示緩沖區(qū)內(nèi)容
    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). 控制臺(tái)窗口操作

Win32 API提供了對(duì)控制臺(tái)窗口及其緩沖區(qū)的可控制.

Screen buffer and console window.png

有幾個(gè)函數(shù)會(huì)影響控制臺(tái)窗口及其相對(duì)于屏幕緩沖區(qū)的位置:
—— SetConsoleWindowInfo設(shè)置控制臺(tái)窗口相對(duì)于屏幕緩沖區(qū)的大小和位置。
—— GetConsoleScreenBufferInfo返回控制臺(tái)窗口相對(duì)于屏幕緩沖區(qū)的矩形坐標(biāo)(以及其他內(nèi)容)丽蝎。
—— SetConsoleCursorPosition將光標(biāo)位置設(shè)置為屏幕緩沖區(qū)內(nèi)的任何位置; 如果該區(qū)域不可見猎拨,則移動(dòng)控制臺(tái)窗口以使光標(biāo)可見。
—— ScrollConsoleScreenBuffer移動(dòng)屏幕緩沖區(qū)中的部分或全部文本屠阻,這會(huì)影響控制臺(tái)窗口中顯示的文本

  • SetConsoleTitle 函數(shù)
    SetConsoleTitle函數(shù)可讓我們改變控制他窗口的標(biāo)題红省。
.data
    titleStr BYTE "Console title",0
.code
    INVOKE SetConsoleTitle, ADDR titleStr
  • GetConsoleScreenBufferInfo 函數(shù)
    GetConsoleScreenBufferInfo函數(shù)返回控制臺(tái)窗口的當(dāng)前狀態(tài)信息。 它有量參數(shù):一個(gè)是控制臺(tái)窗口句柄国觉,另一個(gè)是指向由函數(shù)填充的結(jié)構(gòu)的指針吧恃。
GetConsoleScreenBufferInfo PROTO,
    hConsoleOutput:HANDLE,
    lpConsoleScreenBufferInfo:PTR CONSOLE_SCREEN_BUFFER_INFO

其中CONSOLE_SCREEN_BUFFER_INFO結(jié)構(gòu)體定義內(nèi)容為:

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 函數(shù)
    SetConsoleWindowInfo函數(shù)允許您設(shè)置控制臺(tái)窗口相對(duì)于其屏幕緩沖區(qū)的大小和位置。
SetConsoleWindowInfo PROTO,
    hConsoleOutput:HANDLE,          ; screen buffer handle
    bAbsolute:DWORD,                ; coordinate type
    lpConsoleWindow:PTR SMALL_RECT  ; ptr to window rectangle

bAbsolute指示如何使用lpConsoleWindow指向的結(jié)構(gòu)中的坐標(biāo)麻诀。 如果bAbsolute為true痕寓,則坐標(biāo)指定控制臺(tái)窗口的新左上角和右下角。 如果bAbsolute為false蝇闭,則坐標(biāo)將添加到當(dāng)前窗口坐標(biāo)呻率。

; 滾動(dòng)控制臺(tái)窗口

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 函數(shù)
    SetConsoleScreenBufferSize函數(shù)允許您將屏幕緩沖區(qū)大小設(shè)置為X列Y行。
SetConsoleScreenBufferSize PROTO,
    hConsoleOutput:HANDLE,  ; handle to screen buffer
    dwSize:COORD            ; new screen buffer size
8). 控制光標(biāo)

Win32 API提供了設(shè)置光標(biāo)大小呻引,可見性和屏幕位置的功能礼仗。 與這些函數(shù)相關(guān)的重要數(shù)據(jù)結(jié)構(gòu)是CONSOLE_CURSOR_INFO,其中包含有關(guān)控制臺(tái)光標(biāo)大小和可見性的信息逻悠。

CONSOLE_CURSOR_INFO STRUCT
    dwSize DWORD ?
    bVisible DWORD ?
CONSOLE_CURSOR_INFO ENDS

dwSize是光標(biāo)填充的字符單元格的百分比(1到100)元践。 如果光標(biāo)可見,則bVisible等于TRUE(1)童谒。

  • GetConsoleCursorInfo 函數(shù)
    GetConsoleCursorInfo 函數(shù)返回光標(biāo)的可見性和大小单旁。
GetConsoleCursorInfo PROTO,
    hConsoleOutput:HANDLE,
    lpConsoleCursorInfo:PTR CONSOLE_CURSOR_INFO
  • SetConsoleCursorInfo 函數(shù)
    SetConsoleCursorInfo 函數(shù)設(shè)置光標(biāo)的可見與大小。
SetConsoleCursorInfo PROTO,
    hConsoleOutput:HANDLE,
    lpConsoleCursorInfo:PTR CONSOLE_CURSOR_INFO
  • SetConsoleCursorPosition 函數(shù)
    SetConsoleCursorPosition 函數(shù)設(shè)置光標(biāo)的位置惠啄。
SetConsoleCursorPosition PROTO,
    hConsoleOutput:DWORD,   ; input mode handle
    dwCursorPosition:COORD  ; screen X,Y coordinates
9). 控制字體顏色
  • SetConsoleTextAttribute 函數(shù)
    SetConsoleTextAttribute 函數(shù)允許您將所有后續(xù)文本輸出的前景色和背景色設(shè)置到控制臺(tái)窗口.
SetConsoleTextAttribute PROTO,
    hConsoleOutput:HANDLE,  ; console output handle
    wAttributes:WORD        ; color attribute
  • WriteConsoleOutputAttribute 函數(shù)
    WriteConsoleOutputAttribute 函數(shù)從指定位置開始,將一組屬性值復(fù)制到控制臺(tái)屏幕緩沖區(qū)的連續(xù)單元格。
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指向一個(gè)屬性數(shù)組撵渡,其中每個(gè)屬性的低位字節(jié)包含顏色
nLength是數(shù)組的長(zhǎng)度
dwWriteCoord是接收屬性的起始屏幕單元格
lpNumberOfAttrsWritten指向一個(gè)變量融柬,該變量將保存寫入的單元格數(shù)

; 更改文本顏色

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). 時(shí)間和日期函數(shù)

Win32 API提供了相當(dāng)多的時(shí)間和日期函數(shù)選擇。

Win32 DateTime Functions.png
  • SYSTEMTIME 結(jié)構(gòu)體
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 函數(shù)
    GetLocalTime函數(shù)根據(jù)系統(tǒng)時(shí)鐘返回日期和當(dāng)前時(shí)間。SetLocalTime函數(shù)設(shè)置系統(tǒng)的本地日期和時(shí)間节腐。
GetLocalTime PROTO,
    lpSystemTime:PTR SYSTEMTIME

SetLocalTime PROTO,
    lpSystemTime:PTR SYSTEMTIME

示例:

.data
    sysTime SYSTEMTIME <>
.code
    INVOKE GetLocalTime, ADDR sysTime
  • GetTickCount 函數(shù)
    GetTickCount函數(shù)返回自系統(tǒng)啟動(dòng)以來經(jīng)過的毫秒數(shù)外盯。
GetTickCount PROTO ; return value in EAX

示例: 計(jì)算經(jīng)過時(shí)間

; 計(jì)算經(jīng)過時(shí)間

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 函數(shù)
    程序有時(shí)需要暫停或延遲很短的時(shí)間翼雀。 雖然可以構(gòu)建一個(gè)計(jì)算循環(huán)或繁忙循環(huán)來保持處理器忙饱苟,但循環(huán)的執(zhí)行時(shí)間會(huì)因處理器而異。 此外狼渊,繁忙的循環(huán)將不必要地占用處理器箱熬,從而減慢同時(shí)執(zhí)行的其他程序的速度。
Sleep PROTO,
    dwMilliseconds:DWORD
  • GetDateTime 程序
    Irvine32庫(kù)中的GetDateTime過程返回自1601年1月1日以來經(jīng)過的100納秒時(shí)間間隔的數(shù)量狈邑。
;--------------------------------------------------
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 圖形應(yīng)用

Win32 圖形應(yīng)用需要的文件如下圖, 使用/SUBSYSTEM:WINDOWS替換/SUBSYSTEM:CONSOLE(修改方法:在解決方法管理器中城须,項(xiàng)目名上右鍵 -> 屬性 -> 配置屬性 -> 鏈接器 -> 系統(tǒng) -> 子系統(tǒng),屬性 -> 配置屬性 -> 鏈接器 -> 高級(jí) -> 入口點(diǎn)main@0 改為WinMain@0)

Files Required When Building the WinApp Program.png

1). 必要的結(jié)構(gòu)
  • POINT 結(jié)構(gòu)體
    POINT結(jié)構(gòu)指定屏幕上某點(diǎn)的X和Y坐標(biāo)米苹,以像素為單位糕伐。
POINT STRUCT
    ptX DWORD ?
    ptY DWORD ?
POINT ENDS
  • RECT 結(jié)構(gòu)體
    RECT結(jié)構(gòu)定義矩形的邊界。 左側(cè)成員包含矩形左側(cè)的X坐標(biāo)蘸嘶。 頂部成員包含矩形頂部的Y坐標(biāo)良瞧。
RECT STRUCT
    left DWORD ?
    top DWORD ?
    right DWORD ?
    bottom DWORD ?
RECT ENDS
  • MSGStruct 結(jié)構(gòu)體
    MSGStruct結(jié)構(gòu)定義了MS-Windows消息所需的數(shù)據(jù)。
MSGStruct STRUCT
    msgWnd DWORD ?
    msgMessage DWORD ?
    msgWparam DWORD ?
    msgLparam DWORD ?
    msgTime DWORD ?
    msgPt POINT <>
MSGStruct ENDS
  • WNDCLASS結(jié)構(gòu)體
    WNDCLASS結(jié)構(gòu)定義了一個(gè)窗口類亏较。 程序中的每個(gè)窗口都必須屬于一個(gè)類莺褒,每個(gè)程序必須為其主窗口定義一個(gè)窗口類。
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 程序

每個(gè)Windows應(yīng)用程序都需要一個(gè)啟動(dòng)過程雪情,通常名為WinMain遵岩,它負(fù)責(zé)以下任務(wù):
?獲取當(dāng)前程序的句柄。
?加載程序的圖標(biāo)和鼠標(biāo)光標(biāo)巡通。
?注冊(cè)程序的主窗口類尘执,并確定將處理窗口事件消息的過程。
?創(chuàng)建主窗口宴凉。
?顯示和更新主窗口誊锭。
?開始接收和發(fā)送消息的循環(huán)。 循環(huán)繼續(xù)弥锄,直到用戶關(guān)閉應(yīng)用程序窗口丧靡。

3). WinProc 程序

WinProc過程接收并處理與窗口有關(guān)的所有事件消息蟆沫。 大多數(shù)事件由用戶通過單擊并拖動(dòng)鼠標(biāo),按鍵盤鍵等啟動(dòng)温治。

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饭庞,用戶按下鼠標(biāo)左鍵時(shí)生成
?WM_CREATE,表示剛剛創(chuàng)建了主窗口
?WM_CLOSE熬荆,表示應(yīng)用程序的主窗口即將關(guān)閉

4). ErrorHandler 程序

如果系統(tǒng)在注冊(cè)和創(chuàng)建程序主窗口期間報(bào)告錯(cuò)誤舟山,則會(huì)調(diào)用ErrorHandler過程(可選)。

ErrorHandler過程有幾個(gè)重要的任務(wù)要執(zhí)行:
?調(diào)用GetLastError以檢索系統(tǒng)錯(cuò)誤號(hào)卤恳。
?調(diào)用FormatMessage以檢索適當(dāng)?shù)南到y(tǒng)格式錯(cuò)誤消息字符串累盗。
?調(diào)用MessageBox以顯示包含錯(cuò)誤消息字符串的彈出消息框。
?調(diào)用LocalFree以釋放錯(cuò)誤消息字符串使用的內(nèi)存突琳。

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. 動(dòng)態(tài)申請(qǐng)內(nèi)存

動(dòng)態(tài)內(nèi)存分配(也稱為堆分配)是一種編程語言用于在創(chuàng)建對(duì)象若债,數(shù)組和其他結(jié)構(gòu)時(shí)保留內(nèi)存的技術(shù)。
C本今,C ++和Java具有內(nèi)置的運(yùn)行時(shí)堆管理器拆座,用于處理存儲(chǔ)分配和釋放的編程請(qǐng)求。 堆啟動(dòng)管理器通常在程序啟動(dòng)時(shí)從操作系統(tǒng)分配大塊內(nèi)存冠息。 他們創(chuàng)建了一個(gè)指向存儲(chǔ)塊的免費(fèi)指針列表挪凑。 當(dāng)收到分配請(qǐng)求時(shí),堆管理器將適當(dāng)大小的內(nèi)存塊標(biāo)記為保留逛艰,并返回指向該塊的指針躏碳。 稍后,當(dāng)收到對(duì)同一塊的刪除請(qǐng)求時(shí)散怖,堆會(huì)釋放該塊菇绵,并將其返回到空閑列表。 每次收到新的分配請(qǐng)求時(shí)镇眷,堆管理器都會(huì)掃描空閑列表咬最,查找足夠大的第一個(gè)可用塊以授予請(qǐng)求。

Heap-Related Functions.png
  • GetProcessHeap
    如果您滿足于使用當(dāng)前程序擁有的默認(rèn)堆欠动,則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
    創(chuàng)建私有堆空間
HeapCreate PROTO,
    flOptions:DWORD, ; heap allocation options
    dwInitialSize:DWORD, ; initial heap size, in bytes
    dwMaximumSize:DWORD ; maximum heap size, in bytes

flOptions設(shè)置為NULL
dwInitialSize設(shè)置為初始堆大小(以字節(jié)為單位)具伍。 該值向上舍入到下一頁邊界
HeapAlloc調(diào)用時(shí)超過初始堆大小時(shí)翅雏,它將增大到您在dwMaximumSize參數(shù)中指定的值(向上舍入到下一頁邊界)。 調(diào)用它之后人芽,EAX中的空返回值表示未創(chuàng)建堆

示例:

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 函數(shù)銷毀已存在的私有堆空間
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 函數(shù)在已存在的堆中申請(qǐng)內(nèi)存塊
HeapAlloc PROTO,
    hHeap:HANDLE,   ; handle to existing heap block
    dwFlags:DWORD,  ; heap allocation control flags
    dwBytes:DWORD   ; number of bytes to allocate

hHeap一個(gè)由GetProcessHeap或HeapCreate初始化的堆的32位句柄望几。
dwFlags包含一個(gè)或多個(gè)標(biāo)志值的雙字。 您可以選擇將其設(shè)置為HEAP_ZERO_MEMORY萤厅,它將內(nèi)存塊設(shè)置為全零橄抹。
dwBytes靴迫,一個(gè)雙字,表示堆分配的大小楼誓,以字節(jié)為單位

.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 函數(shù)在已經(jīng)存在的內(nèi)存塊中釋放私有空間矢劲。
HeapFree PROTO,
    hHeap:HANDLE,
    dwFlags:DWORD,
    lpMem:DWORD

示例:

INVOKE HeapFree, hHeap, 0, pArray
  • Error Handling
    如果在調(diào)用HeapCreate,HeapDestroy或GetProcessHeap時(shí)遇到錯(cuò)誤慌随,可以通過調(diào)用GetLastError API函數(shù)來獲取詳細(xì)信息。
INVOKE HeapCreate, 0,HEAP_START, HEAP_MAX
.IF eax == NULL ; failed?
    call WriteWindowsMsg ; show error message
.ELSE
    mov hHeap,eax ; success
.ENDIF
1). 堆測(cè)試程序
; 堆測(cè)試
; 程序動(dòng)態(tài)申請(qǐng)1000個(gè)字節(jié)的內(nèi)存
INCLUDE Irvine32.inc

.data
    ARRAY_SIZE = 1000
    FILL_VAL EQU 0FFh
    hHeap HANDLE ?      ; 進(jìn)程堆句柄
    pArray DWORD ?      ; 指向內(nèi)存塊
    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             ; 失敗阁猜,設(shè)置CF=1
    call WriteWindowsMsg
    call Crlf
    jmp quit
arrayOk:                    ; 字節(jié)填充
    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
; 為數(shù)組動(dòng)態(tài)申請(qǐng)內(nèi)存空間
; 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             ; 返回時(shí)CF=1
    .ELSE
        mov pArray,eax  ; 保存指針
        clc             ; 返回時(shí)CF=0
    .ENDIF
    ret
allocate_array ENDP

;--------------------------------------------------------
fill_array PROC USES ecx edx esi
; 填充單個(gè)字符
; Receives: nothing
; Returns: nothing
;--------------------------------------------------------
    mov ecx,ARRAY_SIZE  ; 循環(huán)計(jì)數(shù)
    mov esi,pArray      ; 數(shù)組指針
    L1: mov BYTE PTR [esi],FILL_VAL ; 填充每個(gè)字節(jié)
    inc esi             ; 下一個(gè)位置
    loop L1
    ret
fill_array ENDP

;--------------------------------------------------------
display_array PROC USES eax ebx ecx esi
; 顯示數(shù)組
; Receives: nothing
; Returns: nothing
;--------------------------------------------------------
    mov ecx,ARRAY_SIZE  ; 循環(huán)計(jì)數(shù)
    mov esi,pArray      ; 數(shù)組指針
    L1: mov al,[esi]    ; 獲取一個(gè)字節(jié)
    mov ebx,TYPE BYTE
    call WriteHexB      ; 顯示
    inc esi             ; 下一個(gè)位置
    loop L1
    ret
display_array ENDP
END main

4. x86 內(nèi)存管理

  • 將邏輯地址轉(zhuǎn)換為線性地址
  • 將線性地址轉(zhuǎn)換為物理地址(分頁)
1). 線性地址
  • 將邏輯地址轉(zhuǎn)換為線性地址
    多任務(wù)操作系統(tǒng)允許多個(gè)程序(任務(wù))同時(shí)在內(nèi)存中運(yùn)行。 每個(gè)程序都有自己獨(dú)特的數(shù)據(jù)區(qū)域蹋艺。 假設(shè)三個(gè)程序每個(gè)都有一個(gè)偏移量為200h的變量; 三個(gè)變量如何在不共享的情況下彼此分離剃袍? 答案是x86處理器使用一步或兩步過程將每個(gè)變量的偏移量轉(zhuǎn)換為唯一的內(nèi)存位置.
    第一步將段值與變量的偏移量組合在一起以創(chuàng)建線性地址。 該線性地址可以是變量的物理地址捎谨。 但是民效,諸如MS-Windows和Linux之類的操作系統(tǒng)采用稱為分頁的功能,以允許程序使用比計(jì)算機(jī)中物理可用的線性內(nèi)存更多的線性內(nèi)存涛救。 他們必須使用稱為頁面轉(zhuǎn)換的第二步將線性地址轉(zhuǎn)換為物理地址畏邢。
Converting a logical address into a linear address.png
  • 分頁
    分頁是x86處理器的一個(gè)重要特性,它使計(jì)算機(jī)可以運(yùn)行一些本來不適合內(nèi)存的程序組合检吆。 處理器通過最初僅將部分程序加載到內(nèi)存中舒萎,同時(shí)將剩余部分保留在磁盤上來完成此操作。 程序使用的內(nèi)存分為稱為頁面的小單元蹭沛,每個(gè)單元通常為4 KB臂寝。 當(dāng)每個(gè)程序運(yùn)行時(shí),處理器選擇性地從內(nèi)存中卸載非活動(dòng)頁面并加載其他立即需要的頁面摊灭。

  • 描述表
    段描述符可以在兩種類型的表中找到:全局描述符表和本地描述符表咆贬。
    全局描述符表——當(dāng)操作系統(tǒng)在啟動(dòng)期間將處理器切換到保護(hù)模式時(shí),會(huì)創(chuàng)建單個(gè)GDT掏缎。 其基址保存在GDTR(全局描述符表寄存器)中。
    本地描述符表——在多任務(wù)操作系統(tǒng)中萝挤,通常為每個(gè)任務(wù)或程序分配其自己的段描述符表御毅,稱為L(zhǎng)DT。 LDTR寄存器包含程序LDT的地址怜珍。

  • 段描述符詳細(xì)信息
    除了段的基址之外端蛆,段描述符還包含指定段限制和段類型的位映射字段。

Indexing into a local descriptor table.png
2). 頁面翻譯

啟用分頁時(shí)酥泛,處理器必須將32位線性地址轉(zhuǎn)換為32位物理地址今豆。 該過程中使用了三種結(jié)構(gòu):
?頁面目錄:最多1024個(gè)32位頁面目錄條目的數(shù)組嫌拣。
?頁表:最多1024個(gè)32位頁表?xiàng)l目的數(shù)組。
?頁面:4 KB或4 MB的地址空間呆躲。

Translating linear address to physical address.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末异逐,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子插掂,更是在濱河造成了極大的恐慌灰瞻,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辅甥,死亡現(xiàn)場(chǎng)離奇詭異酝润,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)璃弄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門要销,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人夏块,你說我怎么就攤上這事疏咐。” “怎么了脐供?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵浑塞,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我政己,道長(zhǎng)缩举,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任匹颤,我火速辦了婚禮仅孩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘印蓖。我一直安慰自己辽慕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布赦肃。 她就那樣靜靜地躺著溅蛉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪他宛。 梳的紋絲不亂的頭發(fā)上船侧,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音厅各,去河邊找鬼镜撩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛队塘,可吹牛的內(nèi)容都是我干的袁梗。 我是一名探鬼主播宜鸯,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼遮怜!你這毒婦竟也來了淋袖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤锯梁,失蹤者是張志新(化名)和其女友劉穎即碗,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陌凳,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拜姿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冯遂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谒获,死狀恐怖蛤肌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情批狱,我是刑警寧澤裸准,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站赔硫,受9級(jí)特大地震影響炒俱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜爪膊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一权悟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧推盛,春花似錦峦阁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瘪菌,卻和暖如春撒会,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背师妙。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工诵肛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人默穴。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓曾掂,卻偏偏與公主長(zhǎng)得像惫谤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子珠洗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容