Windows10 64位汇编中WriteConsoleA无法多次输出问题
问题原因分析
你代码里的核心错误是第二次调用WriteConsoleA时,把第一次调用的返回值当成了标准输出句柄传入,导致句柄无效,写入操作被系统拒绝。
具体来说:
- 第一次调用
GetStdHandle后,rax里是有效的标准输出句柄 - 调用
WriteConsoleA后,rax会被覆盖成该函数的返回值(BOOL类型,成功时为1,失败时为0) - 第二次调用
WriteConsoleA时,你执行mov rcx, rax,此时rcx的值是1,不是真正的标准输出句柄,所以第二次写入完全无效。
修复方案
需要在首次获取句柄后,把它存到一个不会被后续函数调用覆盖的地方——要么用内存变量,要么用Windows x64调用约定里的非易失性寄存器(比如rbx,这类寄存器在函数调用后会保留原值)。
方案1:用内存变量保存句柄
修改后的完整代码:
section .data sum_msg db 'Sum: ', 0 sum db '15', 0 section .bss num_bytes_written resq 1 stdout_handle resq 1 ; 新增变量存标准输出句柄 section .text global _start extern GetStdHandle extern WriteConsoleA extern ExitProcess _start: ; 获取标准输出句柄 mov ecx, -11 ; STD_OUTPUT_HANDLE call GetStdHandle ; 检查句柄是否有效 cmp rax, -1 je .exit mov [stdout_handle], rax ; 把有效句柄存到内存 ; 输出"Sum: " mov rcx, [stdout_handle] ; 使用保存的句柄 lea rdx, [rel sum_msg] mov r8d, 5 ; 消息长度("Sum: "共5个字节) lea r9, [rel num_bytes_written] call WriteConsoleA ; 输出"15" mov rcx, [stdout_handle] ; 再次用保存的句柄 lea rdx, [rel sum] mov r8d, 2 ; "15"的字节数,不需要包含末尾的0 lea r9, [rel num_bytes_written] call WriteConsoleA ; 循环防止程序直接退出 .loop: nop jmp .loop .exit: ; 退出程序 xor ecx, ecx ; 退出码0 call ExitProcess
方案2:用非易失性寄存器保存句柄
如果不想新增内存变量,可以用rbx(Windows x64调用约定中,rbx、rbp、r12-r15属于非易失性寄存器,函数调用不会修改它们的值):
; ... 其他部分不变 ... _start: ; 获取标准输出句柄 mov ecx, -11 call GetStdHandle cmp rax, -1 je .exit mov rbx, rax ; 把句柄存到rbx ; 输出"Sum: " mov rcx, rbx ; 用rbx里的句柄 ; ... 后续代码不变 ... ; 输出"15" mov rcx, rbx ; 再次用rbx里的句柄 ; ... 后续代码不变 ...
额外注意事项
- Windows x64调用约定规则:
- 函数前4个参数依次放在
rcx、rdx、r8、r9中 - 函数返回值存在
rax里 - 非易失性寄存器可以安全保存跨函数调用的数据,不用怕被覆盖
- 函数前4个参数依次放在
- WriteConsoleA的长度参数:这个函数不处理C风格的空终止字符串,直接按你指定的字节数输出,所以
sum的长度写2就对了,不需要包含末尾的0。
验证修复
重新编译链接:
nasm -f win64 sum.asm -o sum.obj gcc -nostdlib -o sum.exe sum.obj -lkernel32
运行后就能得到预期的Sum: 15输出。
内容的提问来源于stack exchange,提问作者kavi castelo
相关产品推荐
相关产品推荐

