You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MASM x64调用CreateWindowExA返回NULL(错误码87)求助

问题分析与解决方案

错误码87(ERROR_INVALID_PARAMETER)的核心来源有两个,同时存在一些次要代码问题:

1. CreateWindowExA栈参数未按x64调用约定传递

x64 Windows调用约定中,所有参数(即使是32位int类型)都必须以8字节(64位)形式传递。你原代码中对X、Y、nWidth、nHeight使用dword ptr赋值,仅初始化低4字节,高4字节保留栈上的垃圾值,导致这些参数变成无效的超大数值,触发系统参数校验失败。

2. WindowProc调用DefWindowProcA时未传递必要参数

窗口创建过程中会发送WM_NCCREATE、WM_CREATE等消息,你的WindowProc直接调用DefWindowProcA但未传递hWnd、uMsg、wParam、lParam这四个必要参数,导致消息处理返回错误结果,系统终止窗口创建流程。

次要代码错误

  • WNDCLASSA结构体中把cbWndExtra字段错误注释为cbClsExtra,虽值为0不影响功能,但属于代码逻辑混淆。
  • WM_CLOSE处理中未调用PostQuitMessage,会导致窗口关闭后程序无法正常退出。

修正后的代码

ExitProcess proto
MessageBoxA proto
RegisterClassA proto
GetModuleHandleA proto
LoadIconA proto
LoadCursorA proto
GetStockObject proto
CreateWindowExA proto
DefWindowProcA proto
DestroyWindow proto
PostQuitMessage proto
GetLastError proto

.const
WM_CLOSE equ 10h
CS_HREDRAW equ 2h
CS_VREDRAW equ 1h
CS_OWNDC equ 20h
BLACK_BRUSH equ 4
IDI_APPLICATION equ 32512
IDC_ARROW equ 32512
WS_OVERLAPPEDWINDOW equ 0CF0000h

w32_class_name byte "snake_asm_window_class", 0
w32_window_name byte "snake", 0
w32_window_width dword 720
w32_window_height dword 1280

.code
WinMainCRTStartup proc
    ; 栈空间计算:WNDCLASS(72) + 局部变量(32) + CreateWindowExA栈参数(96) + 影子空间(32) = 232字节(16字节对齐)
    sub rsp, 232
    
    ; 获取黑色画刷
    mov ecx, BLACK_BRUSH
    call GetStockObject
    mov qword ptr [rsp + 160], rax
    
    ; 加载应用程序图标
    xor rcx, rcx
    mov edx, IDI_APPLICATION
    call LoadIconA
    mov qword ptr [rsp + 152], rax
    
    ; 加载箭头光标
    xor rcx, rcx
    mov edx, IDC_ARROW
    call LoadCursorA
    mov qword ptr [rsp + 144], rax

    ; 获取当前模块句柄
    xor rcx, rcx
    call GetModuleHandleA
    mov qword ptr [rsp + 136], rax
    
    ; 构造WNDCLASSA结构体
    lea rcx, [rsp + 168]                      ; WNDCLASS首地址
    
    mov dword ptr [rcx + 0], CS_HREDRAW or CS_VREDRAW or CS_OWNDC  ; 窗口类风格
    lea rax, [WindowProc]
    mov qword ptr [rcx + 8], rax              ; 窗口过程指针
    mov dword ptr [rcx + 16], 0               ; cbClsExtra
    mov dword ptr [rcx + 20], 0               ; cbWndExtra
    mov rax, qword ptr [rsp + 136]
    mov qword ptr [rcx + 24], rax             ; hInstance
    mov rax, qword ptr [rsp + 152]
    mov qword ptr [rcx + 32], rax             ; hIcon
    mov rax, qword ptr [rsp + 144]
    mov qword ptr [rcx + 40], rax             ; hCursor
    mov rax, qword ptr [rsp + 160]
    mov qword ptr [rcx + 48], rax             ; hbrBackground
    mov qword ptr [rcx + 56], 0               ; lpszMenuName
    lea rax, w32_class_name
    mov qword ptr [rcx + 64], rax             ; lpszClassName
    
    ; 注册窗口类
    call RegisterClassA
    test rax, rax
    jz exit_proc  ; 可选:处理注册失败的情况
    
    ; 调用CreateWindowExA
    xor rcx, rcx                              ; dwExStyle = 0
    lea rdx, w32_class_name                   ; lpClassName
    lea r8, w32_window_name                   ; lpWindowName
    mov r9d, WS_OVERLAPPEDWINDOW              ; dwStyle
    
    ; 栈上参数(按X、Y、nWidth、nHeight、hWndParent、hMenu、hInstance、lpParam顺序)
    mov qword ptr [rsp + 32], 0               ; X = CW_USEDEFAULT(0)
    mov qword ptr [rsp + 40], 0               ; Y = CW_USEDEFAULT(0)
    mov eax, w32_window_width
    mov qword ptr [rsp + 48], rax             ; nWidth
    mov eax, w32_window_height
    mov qword ptr [rsp + 56], rax             ; nHeight
    mov qword ptr [rsp + 64], 0               ; hWndParent = NULL
    mov qword ptr [rsp + 72], 0               ; hMenu = NULL
    mov rax, qword ptr [rsp + 136]
    mov qword ptr [rsp + 80], rax             ; hInstance
    mov qword ptr [rsp + 88], 0               ; lpParam = NULL
    
    call CreateWindowExA
    test rax, rax
    jz get_error  ; 处理窗口创建失败
    
exit_proc:
    xor rcx, rcx
    call ExitProcess
    
get_error:
    call GetLastError
    ; 可选:输出错误码
    jmp exit_proc
WinMainCRTStartup endp

; 标准x64窗口过程参数:hWnd, uMsg, wParam, lParam
WindowProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    ; 栈对齐:进入函数时rsp偏移8字节(返回地址),sub rsp 40后,rsp+40+8=48(16的倍数),满足对齐要求
    sub rsp, 40

    cmp uMsg, WM_CLOSE
    je handle_wm_close

    ; 将参数传递给DefWindowProcA
    mov rcx, hWnd
    mov rdx, uMsg
    mov r8, wParam
    mov r9, lParam
    call DefWindowProcA
    jmp done

handle_wm_close:
    ; 销毁窗口
    mov rcx, hWnd
    call DestroyWindow
    ; 发送退出消息
    mov rcx, 0
    call PostQuitMessage
    xor rax, rax  ; 返回0表示消息已处理

done:
    add rsp, 40
    ret
WindowProc endp

end

关键修正点说明

  • 栈参数传递:所有CreateWindowExA的栈参数都使用qword ptr赋值,确保64位参数的完整性。
  • WindowProc参数传递:明确声明窗口过程的参数,并将这些参数正确传递给DefWindowProcA,保证消息处理流程正常。
  • 代码可读性优化:增加了常量定义,替换魔法数值,修正结构体注释,添加错误分支处理。
  • 栈空间对齐:调整栈空间大小为16字节对齐的数值,符合x64调用约定要求。

内容的提问来源于stack exchange,提问作者cjxo_0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 00:25:54