我正在尝试学习如何使用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
现在它抛出“触发了一个断点”。
Disassembly:
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
最佳答案
您询问了winapi函数末尾的A
和W
后缀的用途。以A
结尾的函数表示A
nsi,以W
结尾的函数是W
ide。Microsoft以这种方式记录它们:
Unicode和ANSI函数
当微软向windows引入unicode支持时,它通过提供两组并行的api来简化转换,一组用于ansi字符串,另一组用于unicode字符串。例如,有两个函数可以设置窗口标题栏的文本:SetWindowTextA
接受ANSI字符串。SetWindowTextW
接受Unicode字符串。
在代码的第一个版本中
您不分配buffer
所需的空间。你曾经拥有:
buffer db ?
给缓冲区分配了一个字节。应该是:
buffer db 128 DUP(?)
您使用了
STD_OUTPUT_HANDLE
而不是STD_INPUT_HANDLE
ReadConsoleInputA
的最后一个参数是指向将返回所读事件数的DWORD的指针。更改变量名bufferSize
可能会使代码更可读。从ReadConsoleInputA文档中:BOOL WINAPI ReadConsoleInput(
_In_ HANDLE hConsoleInput,
_Out_ PINPUT_RECORD lpBuffer,
_In_ DWORD nLength,
_Out_ LPDWORD lpNumberOfEventsRead
);
如果您只读取键盘,则应使用
ReadConsoleA
因为ReadConsoleInputA
将处理键盘和鼠标事件,并且可能在读取字符串之前过早返回。ReadConsoleA
需要一个额外参数,您可以将其设置为空: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