Smallest possible 64-bit MASM GUI application not working correctly

29 Views Asked by At

I try to make a minimal 64-bit windows gui program in assembly (!). I used the MASM compiler (ml64.exe), and the MASM64 include files (https://masm32.com/board/index.php?topic=10052.0). I do this out of curiosity and to understand how windows / x86 cpu actually works.

Following after this is the whole listing. I compiled this program with: ml64.exe helloworld64.exe /link /subsystem:windows /entry:WinMain

option casemap:none
option prologue:none
option epilogue:none

include win64.inc ; MASM64 include file
include kernel32.inc    ; Handles, modules, paths, etc
include user32.inc      ; Windows, controls, etc

includelib kernel32.Lib
includelib user32.lib

.data
    ClassName byte "MyWinClass", 0, 0, 0
    AppName byte "Testprogramma by Tijs", 0, 0, 0

.data?
    hInstance HINSTANCE ?
    uExitCode dword ?

.code

WinMain proc
    local wc:WNDCLASSEX     ; Create these vars on the stack, hence LOCAL
    local msg:MSG
    local hwnd:HWND
    local sui:STARTUPINFO
    local nCmdShow:dword
    
    ; prologue:
    push rbp
    mov rbp, rsp
    sub rsp, 170h

    ; GetStartupInfoA:
    lea rcx, sui
    call GetStartupInfoA            ; Find out if wShowWindow should be used
    test sui.dwFlags, STARTF_USESHOWWINDOW   
    jz @1
    movzx eax, sui.wShowWindow        ; If the show window flag bit was nonzero, we use wShowWindow
    mov nCmdShow, eax
    jmp @2

@1:
    mov nCmdShow, SW_SHOWDEFAULT        ; Use the default 

@2:
    ; GetModuleHandle:
    mov rcx, NULL           ; Get the instance handle of our app (NULL means ourselves)
    call GetModuleHandle        ; GetModuleHandle will return instance handle in EAX
    mov hInstance, rax

    ; Fill WNDCLASSEX:
    mov wc.cbSize, sizeof WNDCLASSEX    ; Fill in the values in the members of our windowclass
    mov wc.style, CS_HREDRAW or CS_VREDRAW ; Redraw if resized in either dimension
    lea rax, WndProc
    mov wc.lpfnWndProc, rax     ; Our callback function to handle window messages
    mov wc.cbClsExtra, 0        ; No extra class data
    mov wc.cbWndExtra, 0        ; No extra window data
    mov rax, hInstance
    mov wc.hInstance, rax       ; Our instance handle
    mov wc.hbrBackground, COLOR_3DSHADOW+1 ; Default brush colors are color plus one
    mov wc.lpszMenuName, NULL       ; No app menu
    lea rax, ClassName
    mov wc.lpszClassName, rax       ; The window's class name

    ; loadicon
    mov rcx, 0              ; hInstance
    mov rdx, IDI_APPLICATION        ; Use the default application icon (lpIconName)
    call LoadIcon
    mov wc.hIcon, rax
    mov wc.hIconSm, rax

    ; LoadCursor
    mov rcx, 0
    mov rdx, IDC_ARROW          ; Get the default "arrow" mouse cursor
    call LoadCursor
    mov wc.hCursor, rax

    ; RegisterClassExA
    lea rcx, wc
    call RegisterClassExA           ; Register the window class 
    cmp ax, 0               ; Registering failed
    je WinMainRet

    ; CreateWindowExA
    mov rcx, 0              ; Extended style bits, if any
    lea rdx, ClassName          ; The window class name of what we're creating
    lea r8, AppName             ; The window title (our application name)
    mov r9d, WS_OVERLAPPEDWINDOW + WS_VISIBLE ; Window stytle (normal and visible)
    mov eax, CW_USEDEFAULT
    mov dword ptr [rsp + 20], eax       ; X
    mov dword ptr [rsp + 28], eax       ; Y
    mov dword ptr [rsp + 30], 800       ; Our requested width
    mov dword ptr [rsp + 38], 600       ; Our requested height
    mov qword ptr [rsp + 40], NULL      ; Parent window (if we were a child window)
    mov qword ptr [rsp + 48], NULL      ; Menu handle
    mov rax, hInstance
    mov [rsp + 50], rax         ; Our app instance handle
    mov qword ptr [rsp + 58], 0
    call CreateWindowExA
    cmp rax, NULL
    je WinMainRet               ; Fail and bail on NULL handle returned
    mov hwnd, rax           ; Window handle is the result, returned in eax

    ; ShowWindow(hWnd, nCmdShow)
    mov rcx, rax
    mov edx, nCmdShow
    call ShowWindow

    ; UpdateWindow(hWnd)
    mov rcx, hwnd                       ; Force a paint of our window
    call UpdateWindow

MessageLoop:
    lea rcx, msg
    mov rdx, NULL
    mov r8d, 0
    mov r9d, 0
    call GetMessage                     ; Get a message from the application's message queue
    cmp eax, 0                          ; When GetMessage returns 0, it's time to exit
    je DoneMessages

    lea rcx, msg                        ; Translate 'msg'
    call TranslateMessage

    lea rcx, msg                        ; Dispatch 'msg'
    call DispatchMessage

    jmp MessageLoop

DoneMessages:
    mov rax, msg.wParam                 ; Return wParam of last message processed

WinMainRet:
    mov uExitCode, eax

    ; MessageBoxA
    ;mov rcx, NULL
    ;lea rdx, ClassName
    ;lea r8, ClassName
    ;mov r9d, MB_OK
    ;call MessageBoxA

    mov ecx, uExitCode
    call ExitProcess
    
    ; epilogue:
    mov rsp, rbp
    pop rbp
    ret

WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    push rbp
    mov rbp, rsp
    sub rsp, 20h

    cmp edx, WM_DESTROY
    jne NotWMDestroy

    ; WM_DESTROY:
    mov ecx, 0                  ; WM_DESTROY received, post our quit msg
    call PostQuitMessage                ; Quit our application
    xor rax, rax                    ; Return 0 to indicate we handled it
    mov rsp, rbp
    pop rbp
    ret

NotWMDestroy:
    cmp edx, WM_PAINT
    jne Default
    
    ; WM_PAINT:
    xor rax, rax
    mov rsp, rbp
    pop rbp
    ret

Default:
    ; DEFAULT:
    call DefWindowProc              ; Forward message on to default processing and
    mov rsp, rbp
    pop rbp
    ret                     ;   return whatever it does

WndProc endp

end

The program works, but the window is not initialized correctly. The window is the smallest possible size, with only the minimize, resize and close buttons visible. The window should be 800x600 pixels in size. I want to make a program with minimum filesize. But I don't know how to debug this program. EDIT: Jester pointed out that the numbers in mov dword ptr [rsp + 20], eax and the lines after that are in decimal, but they should be in hexadecimal (mov dword ptr [rsp + 20h], eax)

0

There are 0 best solutions below