我正在尝试使用MASM/w Visual Studio 2019重新创建类似于以下C++代码的东西。基本上在这个阶段,只希望窗口是可移动的,关闭按钮可以工作。
#include <iostream>
#include <Windows.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
int main()
{
wchar_t windowclass[] = L"MyWinTest";
HINSTANCE hInstance = GetModuleHandleW(NULL);
MSG msg;
WNDCLASSEXW wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = 0;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wc.hIconSm = NULL;
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = windowclass;
wc.lpszMenuName = nullptr;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClassExW(&wc);
HWND hWnd = CreateWindowExW(
0,
windowclass,
L"MyWindow",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
800,
600,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
在我的使用 MASM 的 x64 程序集版本中,窗口被创建并显示,WndProc 也被击中,可以看到它收到消息WM_CREATE并WM_PAINT但窗口无法移动且无法关闭。我确实将C++版本编译为 x64 并输出程序集列表进行比较,但据我所知,它看起来与程序集版本非常相似。
WSTR MACRO lbl:req,qstr:VARARG
LOCAL arg,unq,qot,q
lbl LABEL WORD
FOR arg,<qstr>
qot SubStr <arg>,1,1
q = 0
IFIDNI qot,<!'>;'
q = 1
ELSEIFIDNI qot,<!">;"
q = 1
ELSE
DW arg
ENDIF
IF q EQ 1
unq SubStr <arg>,2,@SizeStr(<arg>)-2
% FORC c,<unq>
DW "&c"
ENDM
ENDIF
ENDM
DW 0
ENDM
L MACRO qstr:VARARG
LOCAL sym,seg
seg EQU <.code>
%IFIDNI <@CurSeg>,<_DATA>
seg EQU <.data>
ENDIF
.CONST
ALIGN 4
WSTR sym,qstr
seg
EXITM <OFFSET sym>
ENDM
extrn LoadCursorW: PROC
extrn MessageBoxW: PROC
extrn ExitProcess: PROC
extrn GetModuleHandleW: PROC
extrn RegisterClassExW: PROC
extrn CreateWindowExW: PROC
extrn GetLastError: PROC
extrn DefWindowProcW: PROC
extrn ShowWindow: PROC
extrn Sleep: PROC
extrn GetMessageW: PROC
extrn TranslateMessage: PROC
extrn DispatchMessageW: PROC
extrn DestroyWindow: PROC
extrn UpdateWindow: PROC
extrn PostQuitMessage: PROC
extrn BeginPaint: PROC
extrn EndPaint: PROC
.data
wstr windowClassName,"AsmTestClass",0,0
wstr windowTitle,"AsmTest",0,0
HWND_DESKTOP textequ <0h>
MB_OK textequ <0h>
INFINITE textequ <0ffffffffh>
WM_CREATE textequ <0001h>
WM_DESTROY textequ <0002h>
WM_SIZE textequ <0005h>
WM_PAINT textequ <000fh>
WM_CLOSE textequ <0010h>
WM_QUIT textequ <0012h>
SW_HIDE textequ <0000h>
SW_SHOW textequ <0005h>
CS_VREDRAW textequ <0001h>
CS_HREDRAW textequ <0002h>
WS_OVERLAPPED textequ <00000000h>
WS_CAPTION textequ <00c00000h>
WS_SYSMENU textequ <00080000h>
WS_MINIMIZEBOX textequ <00020000h>
CW_USEDEFAULT textequ <80000000h>
IDI_APPLICATION textequ <00007f00h>
WINDOW_WIDTH DWORD 800
WINDOW_HEIGHT DWORD 600
WNDCLASSEX STRUCT DWORD
cbSize DWORD ?
style DWORD ?
lpfnWndProc QWORD ?
cbClsExtra DWORD ?
cbWndExtra DWORD ?
hInstance QWORD ?
hIcon QWORD ?
hCursor QWORD ?
hbrBackground QWORD ?
lpszMenuName QWORD ?
lpszClassName QWORD ?
hIconSm QWORD ?
WNDCLASSEX ENDS
MSG STRUCT
hwnd QWORD ?
message DWORD ?
wParam QWORD ?
lParam QWORD ?
time DWORD ?
x DWORD ?
y DWORD ?
MSG ENDS
PAINTSTRUCT STRUCT 8
hdc QWORD ?
fErase DWORD ?
left DWORD ?
top DWORD ?
right DWORD ?
bottom DWORD ?
fRestore DWORD ?
fIncUpdate DWORD ?
rgbReserved BYTE 32 DUP (?)
PAINTSTRUCT ENDS
.code
main proc
LOCAL wc:WNDCLASSEX
LOCAL hWnd:QWORD
LOCAL hInstance:QWORD
LOCAL hCursor:QWORD
LOCAL ATOM:WORD
LOCAL message:MSG
sub rsp, 8
; hInstance = GetModuleHandle(NULL)
mov rcx, 0
call GetModuleHandleW
mov hInstance, rax
; hCursor = LoadCursor(NULL,IDI_APPLICATION)
mov edx, IDI_APPLICATION
xor ecx, ecx
call LoadCursorW
mov hCursor, rax
; Setup Window Class
mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_VREDRAW or CS_HREDRAW
lea rax, OFFSET WndProc
mov wc.lpfnWndProc, rax
mov wc.cbClsExtra, 0
mov wc.cbWndExtra, 0
lea rax, hInstance
mov wc.hInstance, rax
mov wc.hbrBackground, 0
mov wc.lpszMenuName, 0
lea rax, hCursor
mov wc.hCursor, rax
lea rax, windowClassName
mov wc.lpszClassName, rax
mov wc.hIconSm, 0
lea rcx, wc
call RegisterClassExW
mov ATOM, ax
; CreateWindowExW
mov QWORD PTR [rsp+88], 0 ; lpParam
lea rax, hInstance
mov QWORD PTR [rsp+80], rax ; hInstance
mov QWORD PTR [rsp+72], 0 ; hMenu
mov QWORD PTR [rsp+64], 0 ; hWndParent
mov edx, WINDOW_HEIGHT
mov DWORD PTR [rsp+56], edx ; nHeight
mov edx, WINDOW_WIDTH
mov DWORD PTR [rsp+48], edx ; nWidth
mov DWORD PTR [rsp+40], CW_USEDEFAULT ; Y
mov DWORD PTR [rsp+32], CW_USEDEFAULT ; X
mov r9d, WS_OVERLAPPED or WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX ; dwStyle
lea r8, windowTitle ; lpWindowName
lea rdx, windowClassName ; lpClassName
xor ecx,ecx ; dwExStyle
call CreateWindowExW
cmp rax, 0
je WindowFailed
jmp WindowSuccess
WindowFailed:
call GetLastError
; to-do check error ?
WindowSuccess:
mov hWnd, rax
mov rcx, hWnd ; hWnd
mov edx, SW_SHOW ; nCmdShow
call ShowWindow
mov rcx, hWnd ; hWnd
call UpdateWindow
MessageLoop:
xor r9d, r9d ; wMsgFilterMax
xor r8d, r8d ; wMsgFilterMin
xor edx, edx ; hWnd
lea rcx, message ; lpMsg
call GetMessageW
test eax, eax
je QuitMessageLoop
lea rcx, message ; lpMsg
call TranslateMessage
lea rcx, message ; lpMsg
call DispatchMessageW
jmp MessageLoop
QuitMessageLoop:
mov ecx, eax ; uExitCode
call ExitProcess
main endp
WndProc proc
LOCAL hWnd:QWORD
LOCAL uMsg:DWORD
LOCAL wParam:QWORD
LOCAL lParam:QWORD
LOCAL result:QWORD
LOCAL ps:PAINTSTRUCT
LOCAL hdc:QWORD
sub rsp, 8
mov lParam, r9
mov wParam, r8
mov uMsg, edx
mov hWnd, rcx
; msg handler
cmp uMsg,WM_CREATE
je create
cmp uMsg,WM_PAINT
je paint
cmp uMsg,WM_DESTROY
je destroy
; default
mov r9, lParam
mov r8, wParam
mov edx, uMsg
mov rcx, hWnd
call DefWindowProcW
mov result,rax
jmp finish
create:
mov result, 0
jmp finish
paint:
mov rcx, hWnd
lea rdx, ps
call BeginPaint
mov hdc, rax
; to-do HDC paint stuff here
mov rcx, hWnd
lea rdx, ps
call EndPaint
mov result, 0
jmp finish
destroy:
xor ecx, ecx ; nExitCode
call PostQuitMessage
mov result, 0
jmp finish
finish:
mov rax, result
ret
WndProc endp
End
注意由于本地语句 masm 在子 rsp,8 之前自动添加到我的主:(-152 + -8 = -160)
push rbp
mov rbp, rsp
add rsp, 0FFFFFFFFFFFFFF68h
并添加到 WNDPROC 例程 (-120 + -8 = -128)
push rbp
mov rbp, rsp
add rsp, 0FFFFFFFFFFFFFF88h
...
leave
代码错误 - 在两个过程中 -
sub rsp, 8
当从 x64 调用约定
调用方负责为被调用方的参数分配空间。调用方必须始终分配足够的空间来存储 四个寄存器参数,即使被调用方不需要那么多 参数。
所以需要 - 存储四个寄存器参数 - 4*8 和 +8 用于 16 字节对齐,所以
sub rsp, 40
在具体情况下,对DispatchMessageW
和TranslateMessage
的调用可能会损坏(覆盖)message
,因为它位于被调用方的参数空间中。 如果在调试器下进行测试 - 我认为DispatchMessageW
覆盖 Prolog 中的message
,而不是已经使用了错误的message