▶ 书中第十一章的程序,主要讲了 Windows 接口,在小黑框中进行程序交互
● 代码,四种消息框
1 INCLUDE Irvine32.inc 2 3 .data 4 captionW BYTE "Warning", 0 ; 标题 5 warningMsg BYTE "皮一下很开心", 0 ; 消息框内容,支持 Unicode 6 7 captionQ BYTE "Question", 0 8 questionMsg BYTE "皮一下很开心?", 0 9 10 captionC BYTE "Information", 0 11 infoMsg BYTE "那皮一下?", 0dh, 0ah, "不皮也行", 0 12 13 captionH BYTE "Halt", 0 14 haltMsg BYTE "没皮成,被打了", 0 15 16 .code 17 main PROC 18 INVOKE MessageBox, NULL, ADDR warningMsg, ADDR captionW, MB_OK + MB_ICONEXCLAMATION ; 警告图标,OK 按钮 19 20 INVOKE MessageBox, NULL, ADDR questionMsg, ADDR captionQ, MB_YESNO + MB_ICONQUESTION ; 问号图标,Yes / No 按钮 21 cmp eax, IDYES ; 返回值在 eax 中 22 23 INVOKE MessageBox, NULL, ADDR infoMsg, ADDR captionC, MB_YESNOCANCEL + MB_ICONINFORMATION + MB_DEFBUTTON2 ; i 图标,Yes / No / Cancel 按钮 24 25 INVOKE MessageBox, NULL, ADDR haltMsg, ADDR captionH, MB_OK + MB_ICONSTOP ; 叉叉图标, OK 按钮 26 27 call WaitMsg 28 exit 29 main ENDP 30 END main
● 创建 Windows 窗口和相关程序窗口
1 INCLUDE Irvine32.inc 2 INCLUDE GraphWin.inc 3 4 .data 5 AppLoadMsgTitle BYTE "Application Loaded",0 6 AppLoadMsgText BYTE "WM_CREATE message received.",0 7 8 PopupTitle BYTE "Popup Window",0 9 PopupText BYTE "WM_LBUTTONDOWN message received.",0 10 11 GreetTitle BYTE "Main Window",0 12 GreetText BYTE "CreateWindow and UpdateWindow are called.",0 13 14 CloseMsg BYTE "WM_CLOSE message received",0 15 16 ErrorTitle BYTE "Error",0 17 WindowName BYTE "ASM Windows App",0 18 className BYTE "ASMWin",0 19 20 MainWin WNDCLASS <NULL,WinProc,NULL,NULL,NULL,NULL,NULL, COLOR_WINDOW,NULL,className> ; 声明应用的窗口结构 21 22 msg MSGStruct <> 23 winRect RECT <> 24 hMainWnd DWORD ? 25 hInstance DWORD ? 26 27 .code 28 WinMain PROC 29 INVOKE GetModuleHandle, NULL 30 mov hInstance, eax 31 mov MainWin.hInstance, eax 32 33 INVOKE LoadIcon, NULL, IDI_APPLICATION ; 读取图标 34 mov MainWin.hIcon, eax 35 INVOKE LoadCursor, NULL, IDC_ARROW ; 读取光标 36 mov MainWin.hCursor, eax 37 38 INVOKE RegisterClass, ADDR MainWin ; 挂载程序 39 .IF eax == 0 40 call ErrorHandler ; 错误退出 41 jmp Exit_Program 42 .ENDIF 43 44 INVOKE CreateWindowEx, 0, ADDR className, ADDR WindowName, ; 获取主窗口句柄 45 MAIN_WINDOW_STYLE, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL,NULL,hInstance,NULL 46 mov hMainWnd,eax 47 .IF eax == 0 48 call ErrorHandler 49 jmp Exit_Program 50 .ENDIF 51 52 INVOKE ShowWindow, hMainWnd, SW_SHOW ; 绘制主窗口 53 INVOKE UpdateWindow, hMainWnd 54 55 INVOKE MessageBox, hMainWnd, ADDR GreetText, ADDR GreetTitle, ; 扫描键盘 PeekInput.asm 56 INCLUDE Irvine32.inc 57 INCLUDE Macros.inc 58 59 EVENT_BUFFER_SIZE = 1 60 61 .data 62 inputKey BYTE ? 63 stdInHandle DWORD ? 64 65 .code 66 main PROC 67 INVOKE GetStdHandle, STD_INPUT_HANDLE 68 mov stdInHandle, eax 69 INVOKE FlushConsoleInputBuffer, stdInHandle ; 清空控制台输入缓冲区,控制台程序在程序启动时焦点在缓冲区中 70 71 L1: 72 mov eax, 100 ; 等待操作系统时间片(防止程序可以接受外中断以前就已经有键盘输入) 73 call Delay 74 call ReadKey_ ; 读取键盘中断 75 jz L1 ; 通过 ZF 判断是否继续循环 76 77 keyPressed: 78 mWrite "Key code: " ; 返回键盘符号和扫描码 79 mShow DX,hn 80 mWrite "Scan code: " 81 mShow AH,hn 82 83 call Crlf 84 call WaitMsg 85 exit 86 main ENDP 87 88 ReadKey_ PROC 89 90 NUM_KEYS = 1 91 CTRL_KEY = 11h 92 repeatCount TEXTEQU <BYTE PTR [keybuf+4]> ; 从缓冲区的不同部分取出相应的信息 93 virtualKeyCode TEXTEQU <WORD PTR [keybuf+10]> 94 scanCode TEXTEQU <[keybuf+12]> 95 asciiCode TEXTEQU <[keybuf+14]> 96 97 .data 98 keybuf BYTE 50 DUP(0) 99 recordsRead DWORD ? 100 .code 101 INVOKE PeekConsoleInput, ; 速去按键 102 stdInHandle, ADDR keybuf, 1, ADDR recordsRead ; 句柄,缓冲区,按键计数,按键描述 103 cmp recordsRead, 0 104 je quit ; 无效按键,置 ZF = 1 105 106 INVOKE FlushConsoleInputBuffer, stdInHandle ; 清空缓冲区 107 108 cmp WORD PTR keybuf, KEY_EVENT ; 判断是否有键按下 109 jne NoKey 110 111 Check_Count: 112 cmp repeatCount, 1 ; 按键重复数 113 jne NoKey 114 115 Get_Codes: 116 mov ah, scanCode ; 扫描码放入 ah 117 mov al, asciiCode ; ASCII 码放入 al 118 mov dx, virtualKeyCode ; 按键名放入 dx 119 or dx, dx ; 置 ZF = 0 120 jmp quit 121 122 NoKey: ; 无键按下,置 ZF = 1 123 test eax, 0 124 125 quit: 126 ret 127 ReadKey_ ENDP 128 129 END main 130 ; 绘制欢迎界面 131 132 Message_Loop: 133 INVOKE GetMessage, ADDR msg, NULL,NULL,NULL 134 .IF eax == 0 ; eax 无信息时退出 135 jmp Exit_Program 136 .ENDIF 137 138 INVOKE DispatchMessage, ADDR msg ; 主程序中显示信息 139 jmp Message_Loop ; 队列循环等待外中断 140 141 Exit_Program: 142 call waitMsg 143 INVOKE ExitProcess, 0 144 WinMain ENDP 145 146 WinProc PROC, hWnd:DWORD, localMsg:DWORD, wParam:DWORD, lParam:DWORD 147 mov eax, localMsg 148 .IF eax == WM_CREATE ; 创建窗口 149 INVOKE MessageBox, hWnd, ADDR AppLoadMsgText, ADDR AppLoadMsgTitle, MB_OK 150 jmp WinProcExit 151 .ELSEIF eax == WM_LBUTTONDOWN ; 鼠标左键单击 152 INVOKE MessageBox, hWnd, ADDR PopupText, ADDR PopupTitle, MB_OK 153 jmp WinProcExit 154 .ELSEIF eax == WM_CLOSE ; 关闭窗口 155 INVOKE MessageBox, hWnd, ADDR CloseMsg, ADDR WindowName, MB_OK 156 INVOKE PostQuitMessage, 0 157 jmp WinProcExit 158 .ELSE ; 其他信息 159 INVOKE DefWindowProc, hWnd, localMsg, wParam, lParam 160 jmp WinProcExit 161 .ENDIF 162 163 WinProcExit: 164 ret 165 WinProc ENDP 166 167 ErrorHandler PROC 168 .data 169 pErrorMsg DWORD ? ; 错误信息字符串 170 messageID DWORD ? 171 .code 172 INVOKE GetLastError ; 获取错误信息 ID 173 mov messageID,eax 174 175 INVOKE FormatMessage, FORMAT_MESSAGE_ALLOCATE_BUFFER + FORMAT_MESSAGE_FROM_SYSTEM, ; 获取错误信息字符串 176 NULL, messageID, NULL, ADDR pErrorMsg, NULL, NULL 177 178 INVOKE MessageBox, NULL, pErrorMsg, ADDR ErrorTitle, MB_ICONERROR + MB_OK ; 显示错误信息 179 180 INVOKE LocalFree, pErrorMsg ; 释放堆变量 181 ret 182 ErrorHandler ENDP 183 184 END WinMain
● 使用 ReadConsole 从终端输入
1 INCLUDE Irvine32.inc 2 3 BufSize = 80 4 5 .data 6 buffer BYTE BufSize DUP(?) 7 stdInHandle HANDLE ? 8 bytesRead DWORD ? 9 10 .code 11 main PROC 12 INVOKE GetStdHandle, STD_INPUT_HANDLE 13 mov stdInHandle, eax ; 注意保存返回的句柄 14 INVOKE ReadConsole, stdInHandle, ADDR buffer, BufSize, ADDR bytesRead, 0 ; 参数分别为:句柄,存储内存地址,最大字符数,未使用 15 16 mov esi, OFFSET buffer ; 显示保存的字符串 17 mov ecx, bytesRead 18 mov ebx, TYPE buffer 19 call DumpMem ; 注意结尾有 0Dh, 0Ah,即为 " ", 0 20 21 call WaitMsg 22 exit 23 main ENDP 24 END main
● 使用WriteConsole 输出到终端,参数与输入差不多
1 INCLUDE Irvine32.inc 2 3 .data 4 endl EQU <0dh,0ah> ; 换行符 5 6 message LABEL BYTE BYTE "Some words", endl 7 messageSize DWORD ($-message) 8 9 consoleHandle HANDLE 0 10 bytesWritten DWORD ? 11 12 .code 13 main PROC 14 INVOKE GetStdHandle, STD_OUTPUT_HANDLE 15 mov consoleHandle,eax 16 17 INVOKE WriteConsole, consoleHandle, ADDR message, messageSize, ADDR bytesWritten, 0 18 19 call waitmsg 20 INVOKE ExitProcess, 0 21 main ENDP 22 END main
● 终端的各种操作
1 INCLUDE Irvine32.inc 2 3 .data 4 titleStr BYTE "This is title", 0 5 message BYTE ": Something to write on screen buffer", 0dh, 0ah 6 messageSize DWORD ($-message) 7 cursorInfo CONSOLE_CURSOR_INFO <> 8 scrSize COORD <120,60> 9 xyPos COORD <20,5> 10 consoleInfo CONSOLE_SCREEN_BUFFER_INFO <> 11 12 outHandle HANDLE 0 13 bytesWritten DWORD ? 14 lineNum DWORD 0 15 windowRect SMALL_RECT <0,0,60,11> ; 显示窗口在输出缓冲区中的坐标,分别为左、上、右、下,单位是字符 16 17 .code 18 main PROC 19 INVOKE GetStdHandle, STD_OUTPUT_HANDLE 20 mov outHandle, eax 21 22 INVOKE SetConsoleTitle, ADDR titleStr ; 标题 23 24 INVOKE GetConsoleCursorInfo, outHandle, ADDR cursorInfo ; 获取光标信息 25 mov cursorInfo.dwSize, 75 ; 光标缩放为 75% 26 INVOKE SetConsoleCursorInfo, outHandle, ADDR cursorInfo 27 28 INVOKE SetConsoleScreenBufferSize, outHandle,scrSize ; 设置缓冲区尺寸 29 30 INVOKE SetConsoleCursorPosition, outHandle, xyPos ; 设置光标位置 31 32 INVOKE GetConsoleScreenBufferInfo, outHandle, ADDR consoleInfo ; 获取缓冲区和输出窗口信息 33 34 .REPEAT 35 mov eax, lineNum ; 打印行号 36 call WriteDec 37 INVOKE WriteConsole, outHandle, ADDR message, messageSize, ADDR bytesWritten, 0 ; 随便写点什么 38 inc lineNum 39 .UNTIL lineNum > 50 40 41 INVOKE SetConsoleWindowInfo, outHandle, TRUE, ADDR windowRect ; 调整缓冲区窗口位置 42 43 call Readchar ; 等待键盘输入不能用 WaitMsg,否则显示窗口强制重定位到最后 44 call Clrscr ; 清屏 45 call Waitmsg 46 INVOKE ExitProcess, 0 47 main ENDP 48 END main
● 键盘单键测试
1 INCLUDE Irvine32.inc 2 INCLUDE Macros.inc 3 4 .code 5 main PROC 6 INVOKE GetKeyState, VK_NUMLOCK ; 检测数字键盘锁定状态 7 call DumpRegs 8 test al, 1 9 .IF !Zero? 10 mWrite <"NumLock ON",0dh,0ah> 11 .ELSE 12 mWrite <"NumLock OFF",0dh,0ah> 13 .ENDIF 14 15 INVOKE GetKeyState, VK_LSHIFT ; 检测左 Shift 锁定状态 16 call DumpRegs 17 test eax, 80000000h 18 .IF !Zero? 19 mWrite <"L-Shift DOWN",0dh,0ah> 20 .ELSE 21 mWrite <"L-Shift UP",0dh,0ah> 22 .ENDIF 23 24 call WaitMsg 25 exit 26 main ENDP 27 END main
● 返回键盘扫描码和按键
1 INCLUDE Irvine32.inc 2 INCLUDE Macros.inc 3 4 EVENT_BUFFER_SIZE = 1 5 6 .data 7 inputKey BYTE ? 8 stdInHandle DWORD ? 9 10 .code 11 main PROC 12 INVOKE GetStdHandle, STD_INPUT_HANDLE 13 mov stdInHandle, eax 14 INVOKE FlushConsoleInputBuffer, stdInHandle ; 清空控制台输入缓冲区,控制台程序在程序启动时焦点在缓冲区中 15 16 L1: 17 mov eax, 100 ; 等待操作系统时间片(防止程序可以接受外中断以前就已经有键盘输入) 18 call Delay 19 call ReadKey_ ; 读取键盘中断 20 jz L1 ; 通过 ZF 判断是否继续循环 21 22 keyPressed: 23 mWrite "Key code: " ; 返回键盘符号和扫描码 24 mShow DX,hn 25 mWrite "Scan code: " 26 mShow AH,hn 27 28 call Crlf 29 call WaitMsg 30 exit 31 main ENDP 32 33 ReadKey_ PROC 34 35 NUM_KEYS = 1 36 CTRL_KEY = 11h 37 repeatCount TEXTEQU <BYTE PTR [keybuf+4]> ; 从缓冲区的不同部分取出相应的信息 38 virtualKeyCode TEXTEQU <WORD PTR [keybuf+10]> 39 scanCode TEXTEQU <[keybuf+12]> 40 asciiCode TEXTEQU <[keybuf+14]> 41 42 .data 43 keybuf BYTE 50 DUP(0) 44 recordsRead DWORD ? 45 .code 46 INVOKE PeekConsoleInput, ; 速去按键 47 stdInHandle, ADDR keybuf, 1, ADDR recordsRead ; 句柄,缓冲区,按键计数,按键描述 48 cmp recordsRead, 0 49 je quit ; 无效按键,置 ZF = 1 50 51 INVOKE FlushConsoleInputBuffer, stdInHandle ; 清空缓冲区 52 53 cmp WORD PTR keybuf, KEY_EVENT ; 判断是否有键按下 54 jne NoKey 55 56 Check_Count: 57 cmp repeatCount, 1 ; 按键重复数 58 jne NoKey 59 60 Get_Codes: 61 mov ah, scanCode ; 扫描码放入 ah 62 mov al, asciiCode ; ASCII 码放入 al 63 mov dx, virtualKeyCode ; 按键名放入 dx 64 or dx, dx ; 置 ZF = 0 65 jmp quit 66 67 NoKey: ; 无键按下,置 ZF = 1 68 test eax, 0 69 70 quit: 71 ret 72 ReadKey_ ENDP 73 74 END main
● 简单的计时器
1 ; TimingLoop.asm 2 3 INCLUDE Irvine32.inc 4 5 TIME_LIMIT = 5000 6 7 .data 8 startTime DWORD ? 9 dot BYTE ".",0 10 11 .code 12 main PROC 13 INVOKE GetTickCount ; 给一个开始时间 14 mov startTime, eax 15 16 L1: 17 mov edx, OFFSET dot ; 打印一个点 18 call WriteString 19 20 INVOKE Sleep, 100 ; 等待 100 ms 21 22 INVOKE GetTickCount ; 计算经历的时间 23 sub eax, startTime 24 cmp eax, TIME_LIMIT 25 jb L1 26 27 L2: 28 call waitMsg 29 exit 30 main ENDP 31 END main