Netwide Assembler (NASM) >> Assembly (x86) >> Win32 API >> Dialog

Handle Event


參考資訊:
1. nasm
2. masm32

一般在建立Windows視窗時,會註冊自定義的Class並且設定事件處理的副程式(callback),不過,目前範例使用的Class是系統預設的WC_DIALOG,因此,可以透過SetWindowLong()設定callback,這種替換callback的行為就稱為Subclass,意指為原本Class的Subclass,所以Subclass後,需要將原先callback的位址(SetWindowLong()的回傳值)保存下來,然後透過呼叫CallWindowProc()(傳入原先callback的位址),用來將處理控制權移交回原先視窗元件,所以當有多層Subclass的情形發生時,每次在處理事件時,記得判斷對應的Handle,如:在處理關閉事件時(WM_CLOSE),會先判斷目前的hWnd是否為當前的視窗hWnd,如果是當前視窗hWnd,才進行關閉的動作,避免關閉到其它視窗元件

main.asm

    [bits 32]
    global _start

    extern _ExitProcess@4
    extern _GetMessageA@16
    extern _MessageBoxA@16
    extern _DestroyWindow@4
    extern _SetWindowLongA@12
    extern _CallWindowProcA@20
    extern _GetCommandLineA@0
    extern _CreateWindowExA@48
    extern _PostQuitMessage@4
    extern _GetModuleHandleA@4
    extern _DispatchMessageA@4

    %define GetMessage      _GetMessageA@16
    %define MessageBox      _MessageBoxA@16
    %define ExitProcess     _ExitProcess@4
    %define DestroyWindow   _DestroyWindow@4 
    %define SetWindowLong   _SetWindowLongA@12
    %define CallWindowProc  _CallWindowProcA@20
    %define GetCommandLine  _GetCommandLineA@0
    %define CreateWindowEx  _CreateWindowExA@48
    %define PostQuitMessage _PostQuitMessage@4
    %define GetModuleHandle _GetModuleHandleA@4
    %define DispatchMessage _DispatchMessageA@4

    struc POINT
        .x: resd 1
        .y: resd 1
        .SIZE:
    endstruc
 
    struc MSG
        .hwnd:    resd 1
        .message: resd 1
        .wParam:  resd 1
        .lParam:  resd 1
        .time:    resd 1
        .pt:      resb POINT.SIZE
        .SIZE:
    endstruc

    GWL_WNDPROC         equ -4
    WM_DESTROY          equ 2h
    WM_CLOSE            equ 10h
    WC_DIALOG           equ 8002h
    SW_SHOWNORMAL       equ 1h
    WS_OVERLAPPED       equ 0h
    WS_CAPTION          equ 0c00000h
    WS_SYSMENU          equ 80000h
    WS_THICKFRAME       equ 40000h
    WS_MINIMIZEBOX      equ 20000h
    WS_MAXIMIZEBOX      equ 10000h
    WS_VISIBLE          equ 10000000h
    WS_EX_LEFT          equ 00000000h
    WS_OVERLAPPEDWINDOW equ WS_OVERLAPPED | \
                            WS_CAPTION | \
                            WS_SYSMENU | \
                            WS_THICKFRAME | \
                            WS_MINIMIZEBOX | \
                            WS_MAXIMIZEBOX
 
    section .drectve info
_entry    db "/entry:start "
_gdi32    db "/defaultlib:c:\\masm32\\lib\\gdi32.lib "
_user32   db "/defaultlib:c:\\masm32\\lib\\user32.lib "
_kernel32 db "/defaultlib:c:\\masm32\\lib\\kernel32.lib "
  
    segment .data
hWin        dd 0
szCaption   db "main",0
hInstance   dd 0
defWndProc  dd 0
CommandLine dd 0
 
msg istruc MSG
    at .hwnd,    dd 0
    at .message, dd 0
    at .wParam,  dd 0
    at .lParam,  dd 0
    at .time,    dd 0
    at .pt,      dd 0, 0
iend
 
    segment .text
WndProc:
    push ebp
    mov ebp, esp

    cmp dword [ebp + 0ch], WM_CLOSE
    je .handle_close
    cmp dword [ebp + 0ch], WM_DESTROY
    je .handle_destroy
    jmp .handle_default

.handle_close:
    push dword [ebp + 8]
    call DestroyWindow
    xor eax, eax
    jmp .finish

.handle_destroy:
    push 0
    call PostQuitMessage
    xor eax, eax
    jmp .finish

.handle_default:
    push dword [ebp + 14h]
    push dword [ebp + 10h]
    push dword [ebp + 0ch]
    push dword [ebp + 8h]
    push dword [defWndProc]
    call CallWindowProc

.finish:
    leave
    ret 16

WinMain:
    push ebp
    mov ebp, esp
 
    push 0
    push 0
    push 0
    push 0
    push 300
    push 300
    push 0
    push 0
    push WS_OVERLAPPEDWINDOW | WS_VISIBLE
    push szCaption
    push WC_DIALOG
    push WS_EX_LEFT
    call CreateWindowEx
    mov [hWin], eax

    push WndProc
    push GWL_WNDPROC
    push dword [hWin]
    call SetWindowLong
    mov [defWndProc], eax
 
.loop:
    push 0
    push 0
    push 0
    push msg
    call GetMessage
    cmp eax, 0
    je .exit
 
    push msg
    call DispatchMessage
    jmp .loop
 
.exit:
    mov eax, [msg + MSG.wParam]
    leave
    ret 16
 
_start:
    push 0
    call GetModuleHandle
    mov [hInstance], eax
 
    call GetCommandLine
    mov [CommandLine], eax
 
    push SW_SHOWNORMAL
    push dword [CommandLine]
    push 0
    push dword [hInstance]
    call WinMain
 
    push eax
    call ExitProcess

Line 97~107:關閉視窗的順序為:主視窗收到WM_CLOSE事件時,呼叫DestroyWindow(),DestroyWindow()會自動將子視窗的相關資源也一併釋放掉(包含Resource描述的資源),系統接著會發送WM_DESTROY事件給主視窗,待主視窗收到WM_DESTROY事件時,呼叫PostQuitMessage()結束視窗,值得注意的地方是,SendMessage()是屬於Block方式呼叫(必須等待動作執行完畢才返回),而PostQuitMessage()則是Non-block方式呼叫
Line 109~115:將處理控制權移交給下一個視窗元件

Makefile

export WINEPREFIX=/home/user/.wine_amd64

TARGET=main
MYWINE=box86 wine
NASM32=/home/user/.wine_amd64/drive_c/nasm
MASM32=/home/user/.wine_amd64/drive_c/masm32

all:
	${MYWINE} ${NASM32}/bin/nasm.exe -fwin32 ${TARGET}.asm
	${MYWINE} ${MASM32}/bin/link.exe /SUBSYSTEM:WINDOWS /MERGE:.rdata=.text ${TARGET}.obj

run:
	${MYWINE} ${TARGET}.exe

clean:
	rm -rf ${TARGET}.exe ${TARGET}.obj

編譯、執行

$ make
$ make run


返回上一頁