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

请求解释Flat Assembler 64位Windows GUI空窗口代码运行机制

64-bit FASM Windows Empty Window Code Full Breakdown

Hey there! Let's walk through this minimal Win64 GUI template line by line—this is the core structure of almost every Windows GUI app, so understanding each piece will give you a solid foundation for writing more complex Win64 GUI apps in assembly.

First, the Header & Import Section

format PE64 GUI 5.0
entry start
include 'win64a.inc'

section '.idata' import data readable writeable
library kernel32,'KERNEL32.DLL',\
        user32,'USER32.DLL'
include 'api\\kernel32.inc'
include 'api\\user32.inc'
  • format PE64 GUI 5.0: Tells FASM to output a 64-bit Portable Executable (PE) file for the Windows GUI subsystem—this means no console window will pop up alongside your GUI.
  • entry start: Marks start as the program's entry point (where execution begins).
  • include 'win64a.inc': Pulls in pre-defined macros, constants, and type definitions for Win64 assembly (saves you from writing all that boilerplate yourself).
  • The .idata section: Declares the external DLLs (kernel32.dll for core system functions, user32.dll for GUI functions) and imports their APIs—this lets Windows know which system functions your program needs to call.

Data Section (.data)

section '.data' data readable writeable
_title TCHAR 'Win64 program template',0
_class TCHAR 'FASMWIN64',0
_error TCHAR 'Startup failed.',0
wc WNDCLASSEX sizeof.WNDCLASSEX,0,WindowProc,0,0,NULL,NULL,NULL,COLOR_BTNFACE+1,NULL,_class,NULL
msg MSG
  • _title, _class, _error: Wide-character strings (TCHAR is Unicode in Win64) used for the window title, unique window class name, and error message. The trailing 0 marks the end of the string.
  • wc WNDCLASSEX: Initializes a WNDCLASSEX structure—this defines the "blueprint" for your window. Let's break down the initial values:
    • sizeof.WNDCLASSEX: Size of the structure (required for Windows to parse it correctly).
    • 0: Window class style (we'll stick with defaults here).
    • WindowProc: The function that will handle all messages sent to windows of this class.
    • 0,0: Extra class and window data (unused here).
    • NULL: Placeholder for the app instance handle (we'll fill this in later).
    • NULL,NULL: Placeholders for the window icon and cursor (we'll load system defaults later).
    • COLOR_BTNFACE+1: Sets the window's background color to a light gray (standard button face color).
    • NULL: No menu attached to this window class.
    • _class: Links this structure to our unique window class name.
    • NULL: Placeholder for the small icon (we'll set this later).
  • msg MSG: Declares a MSG structure to store messages that Windows sends to our program.

Code Section (.text): Execution Starts Here

section '.text' code readable executable
start:
        sub rsp,8 ; Make stack dqword aligned
        invoke GetModuleHandle,0 ;GetModuleHandle,0?
        mov [wc.hInstance],rax ;wc.hInstance?
        invoke LoadIcon,0,IDI_APPLICATION ;LoadIcon,0,IDI_APPLICATION?
        mov [wc.hIcon],rax ;wc.hIcon?
        mov [wc.hIconSm],rax ;ec.hIconSm?
        invoke LoadCursor,0,IDC_ARROW ;LoadCursor,0,IDC_ARROW?
        mov [wc.hCursor],rax ;wc.hCursor?
        invoke RegisterClassEx,wc ;RegisterClassEx,wc?
        test rax,rax ;test?
        jz error
        invoke CreateWindowEx,0,_class,_title,WS_VISIBLE+WS_DLGFRAME+WS_SYSMENU,128,128,256,192,NULL,NULL,[wc.hInstance],NULL
        test rax,rax
        jz error

Let's unpack your marked questions:

  • sub rsp,8: 64-bit Windows requires the stack to be 16-byte aligned before calling any API. This adjusts the stack pointer to meet that requirement.
  • invoke GetModuleHandle,0: GetModuleHandle(NULL) retrieves the handle of the current executable (your app's instance). Windows needs this to associate your window class with your program.
  • mov [wc.hInstance],rax: Stores the app instance handle in the hInstance field of our WNDCLASSEX structure—this fills in the placeholder we left earlier.
  • invoke LoadIcon,0,IDI_APPLICATION: LoadIcon(NULL, IDI_APPLICATION) loads the default system application icon. Using NULL tells Windows to use a system-wide resource instead of one from our app.
  • mov [wc.hIcon],rax / mov [wc.hIconSm],rax: Assigns the loaded icon to both the main window icon (title bar) and small icon (taskbar).
  • invoke LoadCursor,0,IDC_ARROW: LoadCursor(NULL, IDC_ARROW) loads the default system arrow cursor.
  • mov [wc.hCursor],rax: Assigns the cursor to our window class, so the arrow shows up when hovering over the window.
  • invoke RegisterClassEx,wc: Registers our window class with Windows. Until this is done, we can't create any windows of this type.
  • test rax,rax: A quick way to check if the return value of RegisterClassEx is 0 (which means it failed). The test instruction performs a bitwise AND of rax with itself—if rax is 0, the zero flag is set.
  • jz error: Jumps to the error label if the zero flag is set (i.e., registration failed).
  • invoke CreateWindowEx,...: Creates an actual window instance using our registered class. The parameters are:
    • 0: No extended window styles.
    • _class: Our window class name.
    • _title: The window's title bar text.
    • WS_VISIBLE+WS_DLGFRAME+WS_SYSMENU: Window style—makes the window visible, adds a dialog-style border, and includes the system menu (close/minimize buttons).
    • 128,128: X/Y position of the window's top-left corner.
    • 256,192: Width/height of the window.
    • NULL,NULL: No parent window or menu.
    • [wc.hInstance]: Our app's instance handle.
    • NULL: No extra data to pass to the window.
  • The second test rax,rax and jz error checks if window creation failed (returns 0) and jumps to error handling if so.

The Message Loop (msg_loop)

msg_loop: ;What does this function?
        invoke GetMessage,msg,NULL,0,0
        cmp eax,1
        jb end_loop
        jne msg_loop
        invoke TranslateMessage,msg
        invoke DispatchMessage,msg
        jmp msg_loop

This is the heart of any Windows GUI app—it's responsible for receiving and processing messages from the system:

  • invoke GetMessage,msg,NULL,0,0: Fetches the next message from the system's message queue and stores it in the msg structure. Returns:
    • 1 if a normal message was received.
    • 0 if the WM_QUIT message is received (telling the app to exit).
    • -1 if an error occurred.
  • cmp eax,1 / jb end_loop: If the return value is less than 1 (either 0 or -1), jump to end_loop to exit the program.
  • jne msg_loop: If the return value isn't 1, loop back to fetch the next message (this handles edge cases, though in practice, GetMessage returning 0 would have already triggered the jb jump).
  • invoke TranslateMessage,msg: Converts virtual-key messages (like keyboard presses) into character messages (so you can handle text input later).
  • invoke DispatchMessage,msg: Sends the message to our window procedure (WindowProc) to be processed.
  • jmp msg_loop: Loop back to fetch the next message.

Error Handling & Exit

error:
        invoke MessageBox,NULL,_error,NULL,MB_ICONERROR+MB_OK
end_loop:
        invoke ExitProcess,[msg.wParam]
  • error: If window class registration or window creation fails, pops up an error message box with an exclamation icon and OK button.
  • end_loop: Calls ExitProcess to terminate the program, using the wParam from the WM_QUIT message (usually 0) as the exit code.

Window Procedure (WindowProc)

proc WindowProc uses rbx rsi rdi, hwnd,wmsg,wparam,lparam ;?
; Note that first four parameters are passed in registers,
; while names given in the declaration of procedure refer to the stack
; space reserved for them - you may store them there to be later accessible
; if the contents of registers gets destroyed. This may look like:
; mov [hwnd],rcx
; mov [wmsg],edx
; mov [wparam],r8
; mov [lparam],r9
        cmp edx,WM_DESTROY
        je .wmdestroy
.defwndproc: ;What does this?
        invoke DefWindowProc,rcx,rdx,r8,r9
        jmp .finish
.wmdestroy:
        invoke PostQuitMessage,0
        xor eax,eax
.finish:
        ret
endp

This is the function that handles all messages sent to our window:

  • proc WindowProc uses rbx rsi rdi, hwnd,wmsg,wparam,lparam: Defines the procedure. uses rbx rsi rdi tells FASM to save these registers (they're non-volatile in 64-bit Windows, meaning we have to preserve their values for the caller). The parameters hwnd, wmsg, wparam, lparam are mapped to registers in Win64 calling convention:
    • rcx = hwnd (window handle)
    • rdx = wmsg (message ID, like WM_DESTROY)
    • r8 = wparam (first message-specific parameter)
    • r9 = lparam (second message-specific parameter)
      The comment explains you can store these register values to the stack variables if you need to reuse them after modifying the registers.
  • cmp edx,WM_DESTROY: Checks if the message is WM_DESTROY (sent when the user closes the window).
  • je .wmdestroy: Jumps to the WM_DESTROY handler if the message matches.
  • .defwndproc:: Handles all messages we don't explicitly process. invoke DefWindowProc,rcx,rdx,r8,r9 calls the system's default window procedure, which takes care of standard behavior like resizing, repainting, and closing the window frame. We then jump to .finish to return the result.
  • .wmdestroy:: When the window is closed:
    • invoke PostQuitMessage,0: Sends a WM_QUIT message to the message loop, which triggers the loop to exit.
    • xor eax,eax: Sets the return value to 0 (required for WM_DESTROY handling).
  • .finish:: Returns to the caller (via ret) with the appropriate result in rax.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 11:48:16