我正在尝试学习如何使用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函数末尾的AW后缀的用途。以A结尾的函数表示Ansi,以W结尾的函数是Wide。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

07-24 09:45