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

汇编实现Pong游戏后无法创建窗口的问题求助

问题描述

为深入理解计算机底层运行机制,用汇编语言编写了Pong游戏。此前代码可正常运行,未修改任何代码,关闭误报为恶意程序的杀毒软件并重启电脑后,现在无法创建窗口。编译时仅出现警告hello.asm(51) : warning A6001: no return from procedure,此前该警告不影响运行,目前无其他异常报错,寻求解决方法。

原代码:

.386
.model flat, stdcall
option casemap:none

include C:\MASM32\include\masm32rt.inc
includelib user32.lib
includelib kernel32.lib

.data
    txClass db "MyWinClass", 0
    wcx     WNDCLASSEX <WNDCLASSEX, CS_HREDRAW or CS_VREDRAW, WndProc, 0, 0, 1, 2, 3, COLOR_BTNFACE+1, 0, txClass, 4>
    rect    RECT <20, 0, 40, 100> ; Rectangle to hold the client area dimensions
    ball    RECT <200, 200, 210,210> ; Rectangle to hold the client area dimensions
    ballMX  DWORD 5
    ballMY  DWORD 5
.data? ; uninitialised data - use for handles etc
    hMenu   dd?
    ps PAINTSTRUCT<?>
    dwStartTime DWORD?
    dwElapsedTime DWORD?
    hWnd2  DWORD?
.code
WinMain proc uses ebx
    LOCAL msg:MSG

    
    
    mov ebx, offset wcx
    wc equ [ebx.WNDCLASSEX]
    mov wc.hInstance, rv(GetModuleHandle, 0)
    mov wc.hIcon, rv(LoadIcon, NULL, IDI_APPLICATION)
    mov wc.hIconSm, eax

    invoke RegisterClassEx, addr wc
    invoke CreateWindowEx, NULL, wc.lpszClassName, chr$("Pong"),
        WS_OVERLAPPEDWINDOW or WS_VISIBLE,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, wc.hInstance, NULL

    mov hWnd2, eax
    invoke SetTimer, hWnd2, 1, 8, NULL

   .While 1
        invoke GetMessage, ADDR msg, NULL, 0, 0
       .Break.if!eax
        invoke TranslateMessage, ADDR msg
        invoke DispatchMessage, ADDR msg
   .Endw
    
    exit msg.wParam
WinMain endp

WndProc proc uses esi edi ebx hWnd, uMsg, wParam:WPARAM, lParam:LPARAM
    LOCAL hdc: HDC

    
    SWITCH uMsg

    CASE WM_CREATE
    ret
    CASE WM_KEYDOWN
        mov eax, wParam
        SWITCH eax
        CASE VK_S
            add dword ptr [rect.top], 5
            add dword ptr [rect.bottom], 5
        CASE VK_W
            sub dword ptr [rect.top], 5
            sub dword ptr [rect.bottom], 5
            ret
    ret
    ENDSW
    invoke InvalidateRect, hWnd, NULL, TRUE ; Force the window to repaint
    ret 

    CASE WM_TIMER
        push ebx
        mov eax, ball.bottom
        mov ebx, ps.rcPaint.bottom
        cmp eax,ebx
        jb cont
        neg  ballMY
    cont:
        mov eax,ballMY
        add ball.top, eax     
        add ball.bottom, eax
        
        xor ebx, ebx
        
        mov eax, ball.right
        mov ebx, ps.rcPaint.right
        cmp eax,ebx
        jb cont2
        neg  ballMX
    cont2:
        mov eax,ballMX
        add ball.left, eax     
        add ball.right, eax    
        
        pop ebx
        
        
        
        
        invoke InvalidateRect, hWnd, NULL, TRUE  ; Request a repaint

    CASE WM_PAINT
    
        invoke BeginPaint, hWnd, ADDR ps ; Start painting the window
        mov hdc, eax                   ; Store the device context handle
        invoke CreateSolidBrush, 0000000
        invoke FillRect, hdc, ADDR ps.rcPaint, eax


        invoke CreateSolidBrush, 0FFFFFFh
        invoke FillRect, hdc, ADDR rect, eax
        
        
        invoke CreateSolidBrush, 0FFFFFFh
        invoke FillRect, hdc, ADDR ball, eax

        invoke EndPaint, hWnd, ADDR ps   ; End painting
        ret

    CASE WM_DESTROY
        invoke PostQuitMessage, NULL

    ENDSW
    invoke DefWindowProc, hWnd, uMsg, wParam, lParam
    ret
WndProc endp

end WinMain
解决方法

1. 修复窗口过程(WndProc)的流程错误

原代码中多个消息分支存在ret位置错误或缺失,导致窗口过程逻辑混乱,这是窗口无法创建的核心原因:

  • WM_KEYDOWN分支:VK_W处理后直接ret,VK_S处理后没有ret,且ENDSW位置错误,导致InvalidateRect永远不会执行。调整为所有按键处理完后统一调用InvalidateRect再ret。
  • WM_TIMER分支:处理完后没有ret,会继续执行后续的WM_PAINT代码,引发严重错误,必须添加ret。
  • WM_DESTROY分支:处理完PostQuitMessage后添加ret,避免执行多余的DefWindowProc调用。

2. 修复全局PAINTSTRUCT的错误使用

全局变量ps仅在WM_PAINT中被BeginPaint初始化,WM_TIMER中直接访问ps.rcPaint会读取到未初始化的垃圾值,导致球的碰撞逻辑错误。改用GetClientRect获取窗口客户区大小进行碰撞检测。

3. 修复窗口类的光标初始化错误

原WNDCLASSEX初始化时将hCursor设为3(无效句柄),改为加载系统默认箭头光标,避免窗口类注册异常。

4. 添加错误检查

在RegisterClassEx和CreateWindowEx后添加返回值检查,方便定位窗口创建失败的具体原因。

修改后的代码
.386
.model flat, stdcall
option casemap:none

include C:\MASM32\include\masm32rt.inc
includelib user32.lib
includelib kernel32.lib

.data
    txClass db "MyWinClass", 0
    ; 修复hCursor初始化,使用系统默认箭头光标
    wcx     WNDCLASSEX <WNDCLASSEX, CS_HREDRAW or CS_VREDRAW, WndProc, 0, 0, 0, 0, rv(LoadCursor, NULL, IDC_ARROW), COLOR_BTNFACE+1, 0, txClass, 0>
    rect    RECT <20, 0, 40, 100> ; 玩家 paddle
    ball    RECT <200, 200, 210,210> ; 球
    ballMX  DWORD 5
    ballMY  DWORD 5
    clientRect RECT <?> ; 新增存储客户区大小的变量
.data?
    hMenu   dd?
    ps PAINTSTRUCT<?>
    dwStartTime DWORD?
    dwElapsedTime DWORD?
    hWnd2  DWORD?
.code
WinMain proc uses ebx
    LOCAL msg:MSG
    LOCAL hInst:HINSTANCE

    mov hInst, rv(GetModuleHandle, 0)
    mov ebx, offset wcx
    wc equ [ebx.WNDCLASSEX]
    mov wc.hInstance, hInst
    mov wc.hIcon, rv(LoadIcon, NULL, IDI_APPLICATION)
    mov wc.hIconSm, eax

    ; 检查窗口类注册是否成功
    invoke RegisterClassEx, addr wc
    .if eax == 0
        invoke MessageBox, NULL, chr$("窗口类注册失败"), chr$("错误"), MB_OK
        exit 1
    .endif

    ; 创建窗口并检查是否成功
    invoke CreateWindowEx, NULL, wc.lpszClassName, chr$("Pong"),
        WS_OVERLAPPEDWINDOW or WS_VISIBLE,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, hInst, NULL
    mov hWnd2, eax
   .if eax == 0
        invoke MessageBox, NULL, chr$("窗口创建失败"), chr$("错误"), MB_OK
        exit 1
    .endif

    invoke SetTimer, hWnd2, 1, 8, NULL

   .While 1
        invoke GetMessage, ADDR msg, NULL, 0, 0
       .Break.if!eax
        invoke TranslateMessage, ADDR msg
        invoke DispatchMessage, ADDR msg
   .Endw
    
    exit msg.wParam
WinMain endp

WndProc proc uses esi edi ebx hWnd, uMsg, wParam:WPARAM, lParam:LPARAM
    LOCAL hdc: HDC

    SWITCH uMsg

    CASE WM_CREATE
        ret

    CASE WM_KEYDOWN
        mov eax, wParam
        SWITCH eax
        CASE VK_S
            add dword ptr [rect.top], 5
            add dword ptr [rect.bottom], 5
        CASE VK_W
            sub dword ptr [rect.top], 5
            sub dword ptr [rect.bottom], 5
        ENDSW ; 修正ENDSW位置
        invoke InvalidateRect, hWnd, NULL, TRUE
        ret 

    CASE WM_TIMER
        ; 获取当前窗口客户区大小
        invoke GetClientRect, hWnd, ADDR clientRect
        
        push ebx
        ; 检查球是否碰到上下边界
        mov eax, ball.bottom
        mov ebx, clientRect.bottom
        cmp eax, ebx
        jb contY
        neg ballMY
    contY:
        mov eax, ball.top
        cmp eax, 0
        jge contY2
        neg ballMY
    contY2:
        mov eax, ballMY
        add ball.top, eax     
        add ball.bottom, eax
        
        ; 检查球是否碰到左右边界
        mov eax, ball.right
        mov ebx, clientRect.right
        cmp eax, ebx
        jb contX
        neg ballMX
    contX:
        mov eax, ball.left
        cmp eax, 0
        jge contX2
        neg ballMX
    contX2:
        mov eax, ballMX
        add ball.left, eax     
        add ball.right, eax    
        
        pop ebx
        
        invoke InvalidateRect, hWnd, NULL, TRUE
        ret ; 添加ret,避免执行后续代码

    CASE WM_PAINT
        invoke BeginPaint, hWnd, ADDR ps
        mov hdc, eax
        ; 绘制背景
        invoke CreateSolidBrush, 000000h
        invoke FillRect, hdc, ADDR ps.rcPaint, eax
        invoke DeleteObject, eax ; 释放GDI资源,避免内存泄漏
        
        ; 绘制玩家 paddle
        invoke CreateSolidBrush, 0FFFFFFh
        invoke FillRect, hdc, ADDR rect, eax
        invoke DeleteObject, eax
        
        ; 绘制球
        invoke CreateSolidBrush, 0FFFFFFh
        invoke FillRect, hdc, ADDR ball, eax
        invoke DeleteObject, eax

        invoke EndPaint, hWnd, ADDR ps
        ret

    CASE WM_DESTROY
        invoke KillTimer, hWnd, 1 ; 销毁定时器
        invoke PostQuitMessage, NULL
        ret ; 添加ret

    ENDSW
    invoke DefWindowProc, hWnd, uMsg, wParam, lParam
    ret
WndProc endp

end WinMain

额外说明

  • 新增了DeleteObject调用释放GDI画笔资源,避免内存泄漏。
  • 完善了球的边界碰撞检测,增加了顶部和左边界的判断。
  • 添加了定时器销毁逻辑,程序退出时清理资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 11:58:09