我正在尝试学习如何使用Windows api(而不是仅使用C调用,irvine32或masm32),并且遇到了ReadConsoleInputA的问题(WriteConsoleA可以正常工作)。
此外,我不明白为什么在该函数的PROC原型中,大多数示例在ReadConsoleInput / WriteConsole的末尾附加A或W,您能解释为什么吗?
.data
consoleOutHandle dd ?
consoleInHandle dd ?
bufferlen dd ?
buffer db ?
bufferSize DWORD ?
message db "Enter a number:", 0
lmessage equ $-message
.code
main PROC
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov consoleOutHandle, eax
invoke ReadConsoleInputA, consoleOutHandle, offset buffer, 128, bufferSize
main endp
end main
它抛出:访问冲突写入位置0x00000004。
按照Michael Petch的建议,我现在有以下代码:
.data
consoleOutHandle dd ?
consoleInHandle dd ?
byteswritten dd ?
bufferlen dd ?
buffer db 128 DUP(?)
bufferSize dd ?
message db "Enter a number:", 0
lmessage equ $-message
.code
main PROC
invoke GetStdHandle, STD_INPUT_HANDLE
mov consoleInHandle, eax
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov consoleOutHandle, eax
mov eax, lmessage
invoke WriteConsoleA, consoleOutHandle, offset message, eax, bytesWritten, 0
invoke ReadConsoleInputA, consoleInHandle, offset buffer, 128, offset bufferSize
main endp
end main
现在它会引发“触发断点”。
反汇编:
invoke ReadConsoleInputA, consoleInHandle, offset buffer, 128, offset bufferSize
00E71066 push offset bufferSize (0E74090h)
00E7106B push 80h
00E71070 push offset buffer (0E74010h)
00E71075 push dword ptr [consoleInHandle (0E74004h)]
00E7107B call _ReadConsoleInputA@16 (0E7100Ah)
--- No source file -------------------------------------------------------------
00E71080 int 3 **---> Breakpoint here**
00E71081 int 3
答案 0 :(得分:2)
您问WinAPI函数末尾的A
和W
后缀是什么。以A
结尾的函数表示A
nsi,以W
结尾的函数为W
ide。 Microsoft用这种方式记录它们:
Unicode和ANSI函数 当Microsoft向Windows引入Unicode支持时,它通过提供两组并行的API(一组用于ANSI字符串,另一组用于Unicode字符串)简化了过渡。例如,有两个函数可以设置窗口标题栏的文本:
SetWindowTextA
采用ANSI字符串。SetWindowTextW
采用Unicode字符串。
第一版代码
您没有为buffer
分配必要的空间。你有:
buffer db ?
那为缓冲区分配了一个字节。应该是:
buffer db 128 DUP(?)
您使用的是STD_OUTPUT_HANDLE
而不是STD_INPUT_HANDLE
ReadConsoleInputA
的最后一个参数是指向DWORD的指针,该DWORD将返回读取的事件数。更改变量名称bufferSize
可能会使代码更具可读性。从ReadConsoleInputA文档中:
BOOL WINAPI ReadConsoleInput( _In_ HANDLE hConsoleInput, _Out_ PINPUT_RECORD lpBuffer, _In_ DWORD nLength, _Out_ LPDWORD lpNumberOfEventsRead );
如果仅阅读键盘,则应该使用ReadConsoleA
,因为ReadConsoleInputA
将处理键盘和鼠标事件,并且可能在读取字符串之前过早返回。 ReadConsoleA
需要一个额外的参数,您可以将其设置为NULL:
BOOL WINAPI ReadConsole( _In_ HANDLE hConsoleInput, _Out_ LPVOID lpBuffer, _In_ DWORD nNumberOfCharsToRead, _Out_ LPDWORD lpNumberOfCharsRead, _In_opt_ LPVOID pInputControl );
要退出该程序,您需要调用ExitProcess
。
在第二版代码中
您的代码可以:
invoke WriteConsoleA, consoleOutHandle, offset message, eax, bytesWritten, 0
bytesWritten
必须是一个指针,因为这是一个输出参数。来自WriteConsoleA文档:
BOOL WINAPI WriteConsole( _In_ HANDLE hConsoleOutput, _In_ const VOID *lpBuffer, _In_ DWORD nNumberOfCharsToWrite, _Out_ LPDWORD lpNumberOfCharsWritten, _Reserved_ LPVOID lpReserved );
根据您的第二个代码示例,使用ReadConsoleA
而不是ReadConsoleInputA
的代码版本可能如下:
.data
consoleOutHandle dd ?
consoleInHandle dd ?
bytesWritten dd ?
bufferlen dd ?
buffer db 128 DUP(?)
numEvents dd ?
message db "Enter a number:", 0
lmessage equ $-message
.code
main PROC
invoke GetStdHandle, STD_INPUT_HANDLE
mov consoleInHandle, eax
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov consoleOutHandle, eax
mov eax, lmessage
invoke WriteConsoleA, consoleOutHandle, offset message, eax, offset bytesWritten, 0
invoke ReadConsoleA, consoleInHandle, offset buffer, 128, offset numEvents, 0
invoke ExitProcess, 0
main endp
end main
可以使用MASM的sizeof
运算符来清除此代码。该代码可以写为:
.data
consoleOutHandle dd ?
consoleInHandle dd ?
buffer db 128 DUP(?)
bytesWritten dd ?
numEvents dd ?
message db "Enter a number:", 0
.code
main PROC
invoke GetStdHandle, STD_INPUT_HANDLE
mov consoleInHandle, eax
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov consoleOutHandle, eax
invoke WriteConsoleA, consoleOutHandle, offset message, sizeof message, offset bytesWritten, 0
invoke ReadConsoleA, consoleInHandle, offset buffer, sizeof buffer, offset numEvents, 0
invoke ExitProcess, 0
main endp
end main