我尝试在汇编中制作一个最小的 64 位 Windows GUI 程序(!)。我使用了 MASM 编译器 (ml64.exe) 和 MASM64 包含文件 (https://masm32.com/board/index.php?topic=10052.0)。我这样做是出于好奇并了解 windows / x86 cpu 实际工作原理。
接下来是整个列表。我用以下方法编译了这个程序: 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
程序可以运行,但窗口未正确初始化。该窗口的尺寸尽可能最小,只有最小化、调整大小和关闭按钮可见。窗口大小应为 800x600 像素。 我想制作一个文件大小最小的程序。但我不知道如何调试这个程序。 编辑:小丑指出
mov dword ptr [rsp + 20], eax
中的数字及其后面的行是十进制的,但它们应该是十六进制的 (mov dword ptr [rsp + 20h], eax
)
终于通过安装Masm64 SDK让它工作了。
windows.asm
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include C:\masm64\include64\masm64rt.inc
.data?
hInstance dq ?
hWnd dq ?
hIcon dq ?
hCursor dq ?
sWid dq ?
sHgt dq ?
hBrush dq ?
.data
classname db "template_class",0
caption db "Testprogramma by Tijs",0
.code
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
entry_point proc
GdiPlusBegin ; initialise GDIPlus
mov hInstance, rv(GetModuleHandle,0)
mov hIcon, rv(LoadIcon,hInstance,10)
mov hCursor, rv(LoadCursor,0,IDC_ARROW)
mov sWid, rv(GetSystemMetrics,SM_CXSCREEN)
mov sHgt, rv(GetSystemMetrics,SM_CYSCREEN)
mov hBrush, rv(CreateSolidBrush,00C4C4C4h)
call main
GdiPlusEnd ; GdiPlus cleanup
invoke ExitProcess,0
ret
entry_point endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
main proc
LOCAL wc :WNDCLASSEX
LOCAL lft :QWORD
LOCAL top :QWORD
LOCAL wid :QWORD
LOCAL hgt :QWORD
mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_BYTEALIGNCLIENT or CS_BYTEALIGNWINDOW
mov wc.lpfnWndProc, ptr$(WndProc)
mov wc.cbClsExtra, 0
mov wc.cbWndExtra, 0
mrm wc.hInstance, hInstance
mrm wc.hIcon, hIcon
mrm wc.hCursor, hCursor
mrm wc.hbrBackground, hBrush
mov wc.lpszMenuName, 0
mov wc.lpszClassName, ptr$(classname)
mrm wc.hIconSm, hIcon
invoke RegisterClassEx,ADDR wc
mov wid, 800 ; Set window width to 800
mov hgt, 600 ; Set window height to 600
mov rax, sWid ; calculate offset from left side
sub rax, wid
shr rax, 1
mov lft, rax
mov rax, sHgt ; calculate offset from top edge
sub rax, hgt
shr rax, 1
mov top, rax
; ---------------------------------
; centre window at calculated sizes
; ---------------------------------
invoke CreateWindowEx,WS_EX_LEFT or WS_EX_ACCEPTFILES, \
ADDR classname,ADDR caption, \
WS_OVERLAPPEDWINDOW or WS_VISIBLE,\
lft,top,wid,hgt,0,0,hInstance,0
mov hWnd, rax
call msgloop
ret
main endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
msgloop proc
LOCAL msg :MSG
LOCAL pmsg :QWORD
mov pmsg, ptr$(msg) ; get the msg structure address
jmp gmsg ; jump directly to GetMessage()
mloop:
invoke TranslateMessage,pmsg
invoke DispatchMessage,pmsg
gmsg:
test rax, rv(GetMessage,pmsg,0,0,0) ; loop until GetMessage returns zero
jnz mloop
ret
msgloop endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
WndProc proc hWin:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD
LOCAL dfbuff[260]:BYTE
.switch uMsg
.case WM_COMMAND
.switch wParam
.case 200
invoke SendMessage,hWin,WM_SYSCOMMAND,SC_CLOSE,NULL
.case 300
invoke MsgboxI,hWin, \
"Generic 64 bit Template written with ML64.EXE", \
"About Generic Template",MB_OK,10
.endsw
.case WM_CREATE
.return 0
.case WM_DROPFILES
invoke DragQueryFile,wParam,0,ADDR dfbuff,260
invoke MsgboxI,hWin,ADDR dfbuff,"Drop File Name",MB_OK,10
.case WM_CLOSE
invoke SendMessage,hWin,WM_DESTROY,0,0
.case WM_DESTROY
invoke PostQuitMessage,NULL
.endsw
invoke DefWindowProc,hWin,uMsg,wParam,lParam
ret
WndProc endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end
这假设您已安装 Microsoft Visual Studio 2019。
makeit.bat
@echo on
if not defined DevEnvDir (
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvars64.bat"
)
set appname=window
ml64.exe /c /nologo %appname%.asm
link.exe /SUBSYSTEM:WINDOWS /ENTRY:entry_point /LARGEADDRESSAWARE %appname%.obj