I'm having trouble with the call to BeginPaint(hWnd, lpPaint) in the WindowPaint event. It seems that the function BeginPaint overwrite the stack.
A little C program tell me that the size of PAINTSTRUCT (lpPaint) should be 34.
If i put the adresse to EBP-34 or a little more EBP-56 for the return value of lpPaint, it crash. If I allow alot of stack and put the return adresse to EBP-170 it works.
Simple code:
;Note : All Win32 API functions preserve the EBP, EBX, ESI, and EDI registers
extern GetModuleHandleA
extern ExitProcess
extern GetCommandLineA
extern RegisterClassExA
extern LoadIconA
extern LoadCursorA
extern CreateWindowExA
extern ShowWindow
extern UpdateWindow
extern MessageBoxA
extern GetMessageA
extern TranslateMessage
extern DispatchMessageA
extern PostQuitMessage
extern DefWindowProcA
extern DrawTextA
extern BeginPaint
extern EndPaint
import GetModuleHandleA kernel32.dll
import ExitProcess kernel32.dll
import GetCommandLineA kernel32.dll
import RegisterClassExA user32.dll
import LoadIconA user32.dll
import LoadCursorA user32.dll
import CreateWindowExA user32.dll
import ShowWindow user32.dll
import UpdateWindow user32.dll
import MessageBoxA user32.dll
import GetMessageA user32.dll
import TranslateMessage user32.dll
import DispatchMessageA user32.dll
import PostQuitMessage user32.dll
import DefWindowProcA user32.dll
import DrawTextA user32.dll
import BeginPaint user32.dll
import EndPaint user32.dll
section .text use32
..start:
;Handle from calling process
push dword 0
call [GetModuleHandleA]
mov dword [hInstance], eax
;Get command line
call [GetCommandLineA]
mov dword [commandLine], eax
;Main window
Call WindowMain
;Exit
Push eax
call [ExitProcess]
;========================================================================================
; Create a main windows
;========================================================================================
WindowMain:
push ebp
mov ebp, esp
;Local variable
;==============
;EBP-48 = WNDCLASSEX Structure 48 bytes
;EBP-72 = MSG 24 bytes
;EBP-76 = HWND, handle of our window 4 bytes
sub esp, 76
lea ebx, [ebp-48] ;EBX is now address of WNDCLASSEX
;WNDCLASSEX Structure : http://msdn.microsoft.com/en-us/library/ms633577(v=vs.85).aspx
mov dword [ebx+00], 48 ;Size of the structure
mov dword [ebx+04], 3 ;Style of the window
mov dword [ebx+08], WindowProcedure ;Callback function for events
mov dword [ebx+12], 0
mov dword [ebx+16], 0
mov dword [ebx+20], hInstance ;Handle to the window
;[ebx+24] = HICON
;[ebx+28] = HCURSOR
mov dword [ebx+32], 6 ;Background brush
mov dword [ebx+36], 0 ;Menu name NULL
mov dword [ebx+40], ClassName ;Class name for the window
;[ebx+44] = HICON
;LoadIconA(0, IDI_APPLICATION);
push 32512
push 0
call [LoadIconA]
mov dword [ebx+24], eax ;Icon for our window
mov dword [ebx+44], eax ;Small icon for our window
;LoadCursorA(0, IDC_ARROW);
push dword 32512
push 0
call [LoadCursorA]
mov dword [ebx+28], eax
;RegisterClassExA(WNDCLASSEX address);
push ebx
call [RegisterClassExA]
;CreateWindowEx(0, ClassName, window title, WS_OVERLAPPEDWINDOW, x, y, width, height, handle to parent window, handle to menu, hInstance, NULL);
push 0
push hInstance
push 0
push 0
push 400 ;High
push 500 ;Wide
push dword 0x80000000 ;CW_USEDEFAULT
push dword 0x80000000 ;CW_USEDEFAULT
;WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
push dword 0x00 | 0xC00000 | 0x80000 | 0x40000 | 0x20000 | 0x10000 ;WS_OVERLAPPEDWINDOW
push dword ApplicationName
push dword ClassName
push dword 0
call [CreateWindowExA]
mov dword [ebp-76], eax ;Handle of our window
cmp eax, 0
jz .newWindowsFailed
;ShowWindow(hWind, SW_SHOWDEFAULT);
push dword 10
push dword [ebp-76]
call [ShowWindow]
;UpdateWindow(hWind);
push dword [ebp-76]
call [UpdateWindow]
.MessageLoop:
;GetMessageA(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax);
push dword 0
push dword 0
push dword 0
lea ebx, [ebp-72]
push ebx
call [GetMessageA]
cmp eax, 0 ;WM_QUIT
jz .MessageLoopExit
;TranslateMessage(lpMsg);
lea ebx, [ebp-72]
push ebx
call [TranslateMessage]
;DispatchMessageA(lpMsg);
lea ebx, [ebp-72]
push ebx
call [DispatchMessageA]
jmp .MessageLoop
.MessageLoopExit:
jmp .finish
.newWindowsFailed:
push dword 0
push dword 0
push errMsg
push 0
call [MessageBoxA]
mov eax, 1 ;Return error
mov esp, ebp
pop ebp
ret
.finish:
mov esp, ebp
pop ebp
ret
;========================================================================================
; Handle the events that our window sends us.
;========================================================================================
;LRESULT CALLBACK WindowProc(
; _In_ HWND hwnd,
; _In_ UINT uMsg,
; _In_ WPARAM wParam,
; _In_ LPARAM lParam
;);
;========================================================================================
WindowProcedure:
push ebp
mov ebp, esp
;Local variable
;==============
;EBP-56 = PAINTSTRUCT <- RECALCULER LA TAILLE DE LA STUCTURE!!!
;EBP-60 = HDC
;EBP-76 = RECT
sub esp, 170
mov eax, dword [ebp+12] ;uMsg
cmp eax, 2 ;WM_DESTROY
jz .WindowDestroy
cmp eax, 0X0F ;WM_PAINT
jz .WindowPaint
.WindowDefault:
push dword [ebp+20]
push dword [ebp+16]
push dword [ebp+12]
push dword [ebp+08]
call [DefWindowProcA]
;The return value is from DefWindowProcA so we can't change EAX
mov esp, ebp
pop ebp
ret 16
.WindowDestroy:
;We pass 0 as an argument to the PostQuitMessage() function, to tell it
;to pass 0 as the value of wParam for the next message. At that point,
;GetMessage() will return 0, and the message loop will terminate.
;PostQuitMessage(nExitCode);
push dword 0
call [PostQuitMessage]
jmp .WindowProcedureFinish
.WindowPaint:
;BeginPaint(hWnd, lpPaint);
lea ebx, [ebp-34]
push ebx
push dword [ebp+8]
call [BeginPaint]
mov dword [ebp-60], eax ;Save device context
jmp .WindowProcedureFinish
.WindowProcedureFinish:
xor eax, eax
mov esp, ebp
pop ebp
ret 16
msgBox:
; MessageBoxA(0, msg1, title, 0);
push dword 0
push dword ClassName
push dword TextLabel
push dword 0
call [MessageBoxA]
ret
section .data
ClassName db "Main Window", 0
ApplicationName db "Win32 Assembler", 0
errMsg db "An error occured while making the new window.", 0
TextLabel db "Welcom to the main event!", 13, 10, 13, 10, 0
section .bss
hInstance resd 1
commandLine resd 1
Compile with :
nasm -fobj main.asm
alink -oPE main.obj -o main.exe
A little C program tell me that the size of PAINTSTRUCT (lpPaint)
should be 34
Really? What C compiler is that??? If you look up PAINTSTRUCT on MSDN:
typedef struct tagPAINTSTRUCT {
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT, *PPAINTSTRUCT;
hdc = 4 bytes
fErase = 4 bytes
rcPaint = RECT` = 16 bytes
fRestore = 4 bytes
fIncUpdate = 4 bytes
rgbReserved = 32 bytes
and RECT:
typedef struct _RECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT, *PRECT;
All of that added together gives you 64 bytes!!!
That is why [EBP-56] won't work, but [EBP-170] will work, plenty of room for the structure.
Related
I'm trying to recreate using MASM /w Visual Studio 2019 something similar to the following C++ code which works. Essentially at this stage just want the window to be movable and the close button to work.
#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);
}
}
In my version of x64 assembly using MASM the Window is created and displayed, also WndProc is being hit, can see it get message WM_CREATE and WM_PAINT but the Window can't be moved and can't be closed. I did compile the C++ version as x64 and output assembly listing for comparison, but it looks very similar as far as I can tell to the assembly version.
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
Note due to local statement masm is automatically adding to my main before the sub rsp,8: (-152 + -8 = -160 )
push rbp
mov rbp, rsp
add rsp, 0FFFFFFFFFFFFFF68h
and adding to wndproc routine (-120 + -8 = -128)
push rbp
mov rbp, rsp
add rsp, 0FFFFFFFFFFFFFF88h
...
leave
error in code - in both procs -
sub rsp, 8
when from x64 calling convention
The caller is responsible for allocating space for the callee's parameters. The caller must always allocate sufficient space to store
four register parameters, even if the callee doesn't take that many
parameters.
so need - to store four register parameters - 4*8 and +8 for 16-byte align, so
sub rsp, 40
in concrete case calls to DispatchMessageW and TranslateMessage can corrupt (overwrite) message because it located in callee's parameters space. if test under debugger - i view that DispatchMessageW overwrite message in prolog and than already use wrong message
I'm creating simple game using Win32 API. When I click on window, a ball appear and start rolling look like bida game
My problem is when I call "InvalidateRect", my game very lag. I don't know I'm doing any thing wrong!!!
And my hPen didn't work like what I expect
Please help!!!
Thank you and sorry for my bad English
.386 ; use 80386 instruction
.model flat,stdcall ; uses flat memory addressing model
option casemap:none
include C:\masm32\include\windows.inc ; windows.inc have structures and constants
include C:\masm32\include\user32.inc
includelib C:\masm32\lib\user32.lib ; CreateWindowEx, RegisterClassEx,...
include C:\masm32\include\kernel32.inc
includelib C:\masm32\lib\kernel32.lib ; ExitProcess
include C:\masm32\include\masm32.inc
includelib C:\masm32\lib\masm32.lib
include C:\masm32\include\gdi32.inc
includelib C:\masm32\lib\gdi32.lib
.CONST
DRAWING equ 1
WAITING equ 0
PEN_COLOR equ 00000000h ; black
PEN_SIZE equ 2
BALL_SIZE equ 35
BALL_SPEED equ 20
.DATA
ClassName db 'SimpleWinClass',0
AppName db 'Ball',0
state db WAITING
vectorX dd 6
vectorY dd -7
WIN_WIDTH dd 700
WIN_HEIGHT dd 500
.DATA?
; HINSTANCE & LPSTR typedef DWORD in windows.inc
; reserve the space for future use
hInstance HINSTANCE ?
tlpoint POINT <>
brpoint POINT <>
; use for create window
wc WNDCLASSEX <?>
msg MSG <?> ; handle message
hwnd HWND ? ; handle window procedure
hdc HDC ?
ps PAINTSTRUCT <?>
time SYSTEMTIME <?>
hPen HPEN ?
.CODE
start:
; call GetModuleHandle(null)
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms683199(v=vs.85).aspx
push NULL
call GetModuleHandle ; module handle same as instance handle in Win32
mov hInstance, eax ; return an instance to handle in eax
; call WinMain(hInstance, hPrevInstance, CmdLine, CmdShow)
; our main function
push SW_SHOW
push NULL
push NULL
push hInstance
call WinMain
; call ExitProcess
push eax
call ExitProcess
; Define WinMain
WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
; Structure in msdn, define in windows.inc
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633577(v=vs.85).aspx
; Load default icon
push IDI_APPLICATION
push NULL
call LoadIcon
mov wc.hIcon, eax
mov wc.hIconSm, eax
; Load default cursor
push IDC_ARROW
push NULL
call LoadCursor
mov wc.hCursor, eax
mov wc.cbSize, SIZEOF WNDCLASSEX ; size of this structure
mov wc.style, CS_HREDRAW or CS_VREDRAW ; style of windows https://msdn.microsoft.com/en-us/library/windows/desktop/ff729176(v=vs.85).aspx
mov wc.lpfnWndProc, OFFSET WndProc ; andress of window procedure
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground, COLOR_WINDOW+1 ; background color, require to add 1
mov wc.lpszMenuName, NULL
mov wc.lpszClassName, OFFSET ClassName
; we register our own class, named in ClassName
push offset wc
call RegisterClassEx
; after register ClassName, we use it to create windows compond
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms632680(v=vs.85).aspx
push NULL
push hInstance
push NULL
push NULL
push WIN_HEIGHT
push WIN_WIDTH
push CW_USEDEFAULT
push CW_USEDEFAULT
push WS_OVERLAPPEDWINDOW
push offset AppName
push offset ClassName
push WS_EX_CLIENTEDGE
call CreateWindowEx
mov hwnd, eax ; return windows handle
; display window
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
push CmdShow
push hwnd
call ShowWindow
; update window
; https://msdn.microsoft.com/en-us/library/windows/desktop/dd145167(v=vs.85).aspx
push hwnd
call UpdateWindow
; Message Loop
MESSAGE_LOOP:
; get message
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms644936(v=vs.85).aspx
push PM_REMOVE
push 0
push 0
push NULL
push offset msg
call PeekMessage
; return in eax
; if the function retrieves a message other than WM_QUIT, the return value is nonzero.
; if the function retrieves the WM_QUIT message, the return value is zero.
cmp eax, 0
je GAME_LOOP
cmp msg.message, WM_QUIT
je END_LOOP
; translate virtual-key messages into character messages - ASCII in WM_CHAR
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms644955(v=vs.85).aspx
push offset msg
call TranslateMessage
; sends the message data to the window procedure responsible for the specific window the message is for.
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms644934(v=vs.85).aspx
push offset msg
call DispatchMessage
GAME_LOOP:
; check that is DRAWING or not?
cmp [state], DRAWING
jne MESSAGE_LOOP
push offset time
call GetSystemTime
cmp dword ptr[time.wMilliseconds], BALL_SPEED
jl MESSAGE_LOOP
push TRUE
push NULL
push hwnd
call InvalidateRect
jmp MESSAGE_LOOP
END_LOOP:
mov eax, msg.wParam
ret
WinMain endp
; Handle message with switch(notification)
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633573(v=vs.85).aspx
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
cmp uMsg, WM_PAINT
je ON_WM_PAINT
cmp uMsg, WM_CREATE
je ON_WM_CREATE
cmp uMsg, WM_LBUTTONDOWN
je ON_WM_LBUTTONDOWN
cmp uMsg, WM_DESTROY
je ON_WM_DESTROY
cmp uMsg, WM_QUIT
je ON_WM_DESTROY
cmp uMsg, WM_CLOSE
je ON_WM_DESTROY
jmp ON_DEFAULT
; user close program
ON_WM_DESTROY:
push NULL
call PostQuitMessage
jmp EXIT
ON_WM_CREATE:
; create a pen with specific color and size
; https://msdn.microsoft.com/en-us/library/windows/desktop/dd183509(v=vs.85).aspx
push PEN_COLOR
push PEN_SIZE
push PS_SOLID
call CreatePen
mov hPen, eax
jmp EXIT
ON_WM_LBUTTONDOWN:
cmp [state], DRAWING
je EXIT
push lParam
call updateXY
; when clicked, set state to DRAWING
mov [state], DRAWING
mov dword ptr[time.wMilliseconds], BALL_SPEED
push offset time
call SetSystemTime
jmp EXIT
ON_WM_PAINT:
mov dword ptr[time.wMilliseconds], 0
push offset time
call SetSystemTime
push offset ps
push hWnd
call BeginPaint
mov hdc, eax
; apply pen to hdc
push hPen
push hdc
call SelectObject
call createEllipse
push offset ps
push hWnd
call EndPaint
jmp EXIT
ON_DEFAULT:
; handle any message that program don't handle
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633572(v=vs.85).aspx
push lParam
push wParam
push uMsg ; message
push hWnd ; windows
call DefWindowProc
jmp EXIT
EXIT:
ret
WndProc endp
createEllipse proc
push brpoint.y
push brpoint.x
push tlpoint.y
push tlpoint.x
push hdc
call Ellipse
call moveEllipse
mov eax, WIN_WIDTH
cmp brpoint.x, eax
jg MEET_RIGHT_LEFT
mov eax, WIN_HEIGHT
cmp brpoint.y, eax
jg MEET_BOTTOM_TOP
cmp tlpoint.x, 0
jl MEET_RIGHT_LEFT
cmp tlpoint.y, 0
jl MEET_BOTTOM_TOP
jmp MEET_NONE
MEET_RIGHT_LEFT:
neg vectorX
jmp MEET_NONE
MEET_BOTTOM_TOP:
neg vectorY
jmp MEET_NONE
MEET_NONE:
ret
createEllipse endp
moveEllipse proc
mov eax, dword ptr[vectorX]
mov ecx, dword ptr[vectorY]
add tlpoint.x, eax
add tlpoint.y, ecx
add brpoint.x, eax
add brpoint.y, ecx
ret
moveEllipse endp
updateXY proc lParam:LPARAM
mov eax, lParam
; get low word that contain x
xor ebx, ebx
mov bx, ax
mov tlpoint.x, ebx
mov brpoint.x, ebx
add brpoint.x, BALL_SIZE
; get high word that contain y
mov eax, lParam
shr eax, 16
mov tlpoint.y, eax
mov brpoint.y, eax
add brpoint.y, BALL_SIZE
ret
updateXY endp
end start
It's not a good idea to use the system time for speed issues. Use your procedure moveEllipse instead and increase vectorX and vectorY for more speed. InvalidateRec should be called if there is a real update needed. You can reach that by using a timer instead of changing and checking the system time: SetTimer.
.386 ; use 80386 instruction
.model flat,stdcall ; uses flat memory addressing model
option casemap:none
include C:\masm32\include\windows.inc ; windows.inc have structures and constants
include C:\masm32\include\user32.inc
includelib C:\masm32\lib\user32.lib ; CreateWindowEx, RegisterClassEx,...
include C:\masm32\include\kernel32.inc
includelib C:\masm32\lib\kernel32.lib ; ExitProcess
include C:\masm32\include\masm32.inc
includelib C:\masm32\lib\masm32.lib
include C:\masm32\include\gdi32.inc
includelib C:\masm32\lib\gdi32.lib
.CONST
DRAWING equ 1
WAITING equ 0
PEN_COLOR equ 00000000h ; black
PEN_SIZE equ 2
BALL_SIZE equ 35
BALL_SPEED equ 20
.DATA
ClassName db 'SimpleWinClass',0
AppName db 'Ball',0
state db WAITING
vectorX dd 19
vectorY dd -19
WIN_WIDTH dd 700
WIN_HEIGHT dd 500
.DATA?
; HINSTANCE & LPSTR typedef DWORD in windows.inc
; reserve the space for future use
hInstance HINSTANCE ?
tlpoint POINT <>
brpoint POINT <>
; use for create window
wc WNDCLASSEX <?>
msg MSG <?> ; handle message
hwnd HWND ? ; handle window procedure
hdc HDC ?
ps PAINTSTRUCT <?>
time SYSTEMTIME <?>
hPen HPEN ?
.CODE
start:
; call GetModuleHandle(null)
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms683199(v=vs.85).aspx
push NULL
call GetModuleHandle ; module handle same as instance handle in Win32
mov hInstance, eax ; return an instance to handle in eax
; call WinMain(hInstance, hPrevInstance, CmdLine, CmdShow)
; our main function
push SW_SHOW
push NULL
push NULL
push hInstance
call WinMain
; call ExitProcess
push eax
call ExitProcess
; Define WinMain
WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
; Structure in msdn, define in windows.inc
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633577(v=vs.85).aspx
; Load default icon
push IDI_APPLICATION
push NULL
call LoadIcon
mov wc.hIcon, eax
mov wc.hIconSm, eax
; Load default cursor
push IDC_ARROW
push NULL
call LoadCursor
mov wc.hCursor, eax
mov wc.cbSize, SIZEOF WNDCLASSEX ; size of this structure
mov wc.style, CS_HREDRAW or CS_VREDRAW ; style of windows https://msdn.microsoft.com/en-us/library/windows/desktop/ff729176(v=vs.85).aspx
mov wc.lpfnWndProc, OFFSET WndProc ; andress of window procedure
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground, COLOR_WINDOW+1 ; background color, require to add 1
mov wc.lpszMenuName, NULL
mov wc.lpszClassName, OFFSET ClassName
; we register our own class, named in ClassName
push offset wc
call RegisterClassEx
; after register ClassName, we use it to create windows compond
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms632680(v=vs.85).aspx
push NULL
push hInstance
push NULL
push NULL
push WIN_HEIGHT
push WIN_WIDTH
push CW_USEDEFAULT
push CW_USEDEFAULT
push WS_OVERLAPPEDWINDOW
push offset AppName
push offset ClassName
push WS_EX_CLIENTEDGE
call CreateWindowEx
mov hwnd, eax ; return windows handle
; display window
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
push CmdShow
push hwnd
call ShowWindow
; update window
; https://msdn.microsoft.com/en-us/library/windows/desktop/dd145167(v=vs.85).aspx
push hwnd
call UpdateWindow
; Message Loop
MESSAGE_LOOP:
; get message
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms644936(v=vs.85).aspx
push 0
push 0
push NULL
push offset msg
call GetMessage
; return in eax
; if the function retrieves a message other than WM_QUIT, the return value is nonzero.
; if the function retrieves the WM_QUIT message, the return value is zero.
test eax, eax
jle END_LOOP
; translate virtual-key messages into character messages - ASCII in WM_CHAR
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms644955(v=vs.85).aspx
push offset msg
call TranslateMessage
; sends the message data to the window procedure responsible for the specific window the message is for.
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms644934(v=vs.85).aspx
push offset msg
call DispatchMessage
jmp MESSAGE_LOOP
END_LOOP:
mov eax, msg.wParam
ret
WinMain endp
TimerProc PROC thwnd:HWND, uMsg:UINT, idEvent:UINT, dwTime:DWORD
push TRUE
push NULL
push thwnd
call InvalidateRect
ret
TimerProc ENDP
; Handle message with switch(notification)
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633573(v=vs.85).aspx
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
cmp uMsg, WM_PAINT
je ON_WM_PAINT
cmp uMsg, WM_CREATE
je ON_WM_CREATE
cmp uMsg, WM_LBUTTONDOWN
je ON_WM_LBUTTONDOWN
cmp uMsg, WM_DESTROY
je ON_WM_DESTROY
cmp uMsg, WM_QUIT
je ON_WM_DESTROY
cmp uMsg, WM_CLOSE
je ON_WM_DESTROY
jmp ON_DEFAULT
; user close program
ON_WM_DESTROY:
push NULL
call PostQuitMessage
jmp EXIT
ON_WM_CREATE:
; create a pen with specific color and size
; https://msdn.microsoft.com/en-us/library/windows/desktop/dd183509(v=vs.85).aspx
push PEN_COLOR
push PEN_SIZE
push PS_SOLID
call CreatePen
mov hPen, eax
jmp EXIT
ON_WM_LBUTTONDOWN:
cmp [state], DRAWING
je EXIT
push lParam
call updateXY
; when clicked, set state to DRAWING
mov [state], DRAWING
push OFFSET TimerProc
push 20
push 1
push hwnd
call SetTimer
; mov dword ptr[time.wMilliseconds], BALL_SPEED
; push offset time
; call SetSystemTime
jmp EXIT
ON_WM_PAINT:
mov dword ptr[time.wMilliseconds], 0
push offset ps
push hWnd
call BeginPaint
mov hdc, eax
; apply pen to hdc
push hPen
push hdc
call SelectObject
call createEllipse
push offset ps
push hWnd
call EndPaint
jmp EXIT
ON_DEFAULT:
; handle any message that program don't handle
; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633572(v=vs.85).aspx
push lParam
push wParam
push uMsg ; message
push hWnd ; windows
call DefWindowProc
jmp EXIT
EXIT:
ret
WndProc endp
createEllipse proc
push brpoint.y
push brpoint.x
push tlpoint.y
push tlpoint.x
push hdc
call Ellipse
call moveEllipse
mov eax, WIN_WIDTH
cmp brpoint.x, eax
jg MEET_RIGHT_LEFT
mov eax, WIN_HEIGHT
cmp brpoint.y, eax
jg MEET_BOTTOM_TOP
cmp tlpoint.x, 0
jl MEET_RIGHT_LEFT
cmp tlpoint.y, 0
jl MEET_BOTTOM_TOP
jmp MEET_NONE
MEET_RIGHT_LEFT:
neg vectorX
jmp MEET_NONE
MEET_BOTTOM_TOP:
neg vectorY
jmp MEET_NONE
MEET_NONE:
ret
createEllipse endp
moveEllipse proc
mov eax, dword ptr[vectorX]
mov ecx, dword ptr[vectorY]
add tlpoint.x, eax
add tlpoint.y, ecx
add brpoint.x, eax
add brpoint.y, ecx
ret
moveEllipse endp
updateXY proc lParam:LPARAM
mov eax, lParam
; get low word that contain x
xor ebx, ebx
mov bx, ax
mov tlpoint.x, ebx
mov brpoint.x, ebx
add brpoint.x, BALL_SIZE
; get high word that contain y
mov eax, lParam
shr eax, 16
mov tlpoint.y, eax
mov brpoint.y, eax
add brpoint.y, BALL_SIZE
ret
updateXY endp
end start
You are handling a single message in between running your GAME_LOOP. This does not bode well. Instead, drain the entire message queue in between screen updates. The solution is simple: Add
jmp MESSAGE_LOOP
right after
call DispatchMessage
I try to create the simplest WinAPI window using assembly NASM.
I have problem with Window Proc. Look at commented lines:
%macro API 2
import %1 %2
extern %1
%endmacro
API GetModuleHandleA, kernel32.dll
API LoadIconA,user32.dll
API LoadCursorA,user32.dll
API RegisterClassExA, user32.dll
API CreateWindowExA, user32.dll
API MessageBoxA, user32.dll
API SendMessageA, user32.dll
API DefWindowProcA, user32.dll
API ExitProcess, kernel32.dll
API GetMessageA, user32.dll
API DispatchMessageA, user32.dll
API TranslateMessage,user32.dll
API ShowWindow,user32.dll
API UpdateWindow,user32.dll
API GetCommandLineA,kernel32.dll
API PostQuitMessage,user32.dll
segment .data USE32
windowName db "Hello world!", 0
cmdLine dd 0
hWnd dd 0
hInst dd 0
hCursor dd 0
className db "moje_okno",0
blad db "Blad!!!",0
segment .bss
struc WNDCLASSEX
.sSize resb 4
.style resb 4
.wndProc resb 4
.clsExtra resb 4
.wndExtra resb 4
.hInstance resb 4
.hIcon resb 4
.hCursor resb 4
.background resb 4
.sMenuName resb 4
.sClassName resb 4
.hIconSm resb 4
endstruc
wndClass istruc WNDCLASSEX
iend
global ..start
segment .text USE32
..start:
push 0
call [GetModuleHandleA]
mov dword [hInst], eax ; application handle
push dword 0x00007f00 ; MAKEINTRESOURCE(32512)
push dword 0
call [LoadCursorA]
mov dword [hCursor], eax ; cursor handle
mov dword [wndClass + WNDCLASSEX.sSize], dword 48 ; struct size
mov dword [wndClass + WNDCLASSEX.style], dword 0 ; style
mov dword [wndClass + WNDCLASSEX.wndProc], wndproc ; window proc
mov dword [wndClass + WNDCLASSEX.clsExtra], dword 0
mov dword [wndClass + WNDCLASSEX.wndExtra], dword 0
mov eax, dword [hInst]
mov dword [wndClass + WNDCLASSEX.hInstance], eax ; handle
mov dword [wndClass + WNDCLASSEX.hIcon], dword 0
mov eax, dword [hCursor]
mov dword [wndClass + WNDCLASSEX.hCursor], eax
mov dword [wndClass + WNDCLASSEX.background], dword 0
mov dword [wndClass + WNDCLASSEX.sMenuName], dword 0
mov dword [wndClass + WNDCLASSEX.sClassName], className ; class name
mov dword [wndClass + WNDCLASSEX.hIconSm], dword 0
push wndClass
call [RegisterClassExA]
call near sprawdz_blad ; check return value of RegisterClassExA
push 0 ; param
push dword [hInst] ; handle
push 0 ;hMenu
push 0 ;parent
push 200 ;height
push 200 ;width
push 200 ;y
push 200 ;x
push 0 ;style
push className ;window name
push className ;window class
push 0 ;extended style
call [CreateWindowExA]
push eax
call near sprawdz_blad ;check return value of CreateWindowExA. RETURNS 0
push 0
call [ExitProcess]
wndproc:
; HERE I NEED ACCESS TO WINDOW PROC PARAMETERS: HWND, MSG, WPARAM, LPARAM
; I TRIED:
pop eax
pop ebx
pop ecx
pop edx
; BUT IT DOESN'T WORK
; THERE ARE NOT RIGHT VALUES IN THESE REGISTRIES
ret
box:
push 0
push blad
push blad
push 0
call [MessageBoxA]
ret
sprawdz_blad:
pop eax
cmp eax, 0
jne ok ; if function returns 0 everything is allright
push 0
push blad
push blad
push 0
call [MessageBoxA]
push 1
call [ExitProcess]
ok:
ret
I try to make it work for hours but I'm out of ideas.
Please help.
Greetings, Michal.
A called subroutine, wheether you call it yourself or it's called by Windows (like wndproc) has its return address as the first thing on the stack. You do NOT want to pop this! To access the parameters, you need to look farther up the stack. Try something like...
wndproc:
mov eax, [esp + 4]
mov ebx, [esp + 8]
; etc...
See if that helps...
Best,
Frank
I was trying ICZELION's tutorials and this is my asm program:
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\masm32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\user32.lib
.data
ClassName BYTE "Name of Class",0
MenuName BYTE "Name of Menu",0
TestPrompt BYTE "Test",0
GoodByePrompt BYTE "Good Bye!",0
HelloPrompt BYTE "Hello",0
AppName BYTE "Name Of App",0
.data?
.const
IDM_TEST equ 1
IDM_HELLO equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4
.code
main PROC
push SW_SHOWDEFAULT
call GetCommandLine
push eax
push NULL
push NULL
call GetModuleHandle
push eax
call WinMain
push eax
call ExitProcess
main ENDP
WinMain PROC
;[ebp+014h]CmdShow
;[ebp+010h]commandline
;[ebp+0Ch]hPrevInstance
;[ebp+08h]hInstance
push ebp
mov ebp, esp
add esp, 0FFFFFFB0h
mov DWORD PTR [ebp-030h], SIZEOF WNDCLASSEX
mov DWORD PTR [ebp-02Ch], CS_HREDRAW or CS_VREDRAW
mov DWORD PTR [ebp-028h], OFFSET WinProc
mov DWORD PTR [ebp-024h], NULL
mov DWORD PTR [ebp-020h], NULL
push DWORD PTR [ebp+08h]
pop DWORD PTR [ebp-0Ch]
push IDI_APPLICATION
push NULL
call LoadIcon
mov DWORD PTR [ebp-018h], eax
mov DWORD PTR [ebp-04h], eax
push IDC_ARROW
push NULL
call LoadCursor
mov DWORD PTR [ebp-014h], eax
mov DWORD PTR [ebp-010h], COLOR_WINDOW
mov DWORD PTR [ebp-0Ch], OFFSET MenuName
mov DWORD PTR [ebp-08h], OFFSET ClassName
lea eax, DWORD PTR [ebp-030h]
push eax
call RegisterClassEx
push NULL
push [ebp+08h]
push NULL
push NULL
push 400
push 400
push 200
push 200
push WS_OVERLAPPEDWINDOW
push OFFSET AppName
push OFFSET ClassName
push NULL
call CreateWindowEx
mov [ebp-050h], eax
;display and update the window
push [ebp+014h]
push [ebp-050h]
call UpdateWindow
push [ebp-050h]
call ShowWindow
_MessageLoop:
push NULL
push NULL
push [ebp-050h]
push [ebp-04Ch]
call GetMessage
cmp eax, 0FFFFFFFFh
je _ExitMessageLoop
lea eax, DWORD PTR [ebp-04Ch]
push eax
call TranslateMessage
lea eax, DWORD PTR [ebp-04Ch]
push eax
call DispatchMessage
jmp _MessageLoop
_ExitMessageLoop:
mov eax, [ebp-044h]
mov esp,ebp
pop ebp
ret 010h
WinMain ENDP
WinProc PROC
;[ebp+014h]lParam
;[ebp+010h]wParam
;[ebp+0Ch]uMsg
;[ebp+08h]hWnd
push ebp
mov ebp, esp
cmp DWORD PTR[ebp+0ch], WM_DESTROY
je _WMDESTROY
cmp DWORD PTR [ebp+0Ch], WM_COMMAND
je _WMCOMMAND
_WMDESTROY:
push NULL
call PostQuitMessage
_WMCOMMAND:
mov eax, [ebp+0Ch]
cmp al, IDM_TEST
je _test
cmp al, IDM_HELLO
je _hello
cmp al, IDM_GOODBYE
je _goodbye
cmp al, IDM_EXIT
je _exit
jmp _ExitWmCommand
_test:
push MB_OK
push OFFSET AppName
push OFFSET TestPrompt
push NULL
call MessageBox
jmp _ExitWmCommand
_hello:
push MB_OK
push OFFSET AppName
push OFFSET HelloPrompt
push NULL
call MessageBox
jmp _ExitWmCommand
_goodbye:
push MB_OK
push OFFSET AppName
push OFFSET GoodByePrompt
push NULL
call MessageBox
jmp _ExitWmCommand
_exit:
push [ebp+08h]
call DestroyWindow
_ExitWmCommand:
push DWORD PTR [ebp+014h]
push DWORD PTR [ebp+010h]
push DWORD PTR [ebp+0Ch]
push DWORD PTR [ebp+08h]
call DefWindowProc
xor eax, eax
pop ebp
ret 010h
WinProc ENDP
END main
Here is my resource file, mainprog.rc:
#define IDM_TEST 1
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4
FirstMenu MENU
{
POPUP "&PopUp"
{
MENUITEM "&SayHello", IDM_HELLO
MENUITEM "&SayGoodBye",IDM_GOODBYE
MENUITEM SEPARATOR
MENUITEM "&Exit",IDM_EXIT
}
MENUITEM "&Test", IDM_TEST
}
I'm unable to include the resource file in the program.
What i do is run an ml on the main asm file, then an rc on the resource file and then link them together. The program assembles normally, but when i run the program, it crashes. On debugging, i found that windows could not find the resource data.
Devjeet
The problem is that the MenuName in your .asm is defined as:
MenuName BYTE "Name of Menu",0
But in your resource file it's called 'FirstMenu'.
I suspect that, if you were to change that to:
MenuName BYTE "FirstMenu"
as shown in the example at http://win32assembly.online.fr/tut8.html, the program will work.
I'm trying to manually call RegisterClassEx Windows API without using a WNDCLASS structure on .data section, I need to create this structure only using push instruction.
Could someone help me on that please?
Thanks a lot
In fact you can easily do what you want. You just need to be careful to correctly calculate the addresses of each element of the structure. But this is also an easy task... ;)
Please check out the code I did:
WinMain:
push ebp
mov ebp, esp
add esp, -50h
push 7F00h
push 0h
call LoadIconA
mov ebx, eax
push 7F00h
push 0h
call LoadCursorA
;eax = return of LoadCursorA
;ebx = return of LoadIconA
mov dword ptr ss:[ebp-30h], 30h ;WNDCLASSEX.cbSize, dd WNDCLASSEX_size
mov dword ptr ss:[ebp-2Ch], 3h ;WNDCLASSEX.style, dd CS_VREDRAW + CS_HREDRAW
mov dword ptr ss:[ebp-28h], WndProc ;WNDCLASSEX.lpfnWndProc, dd WndProc
mov dword ptr ss:[ebp-24h], 0h ;WNDCLASSEX.cbClsExtra, dd NULL
mov dword ptr ss:[ebp-20h], 0h ;WNDCLASSEX.cbWndExtra, dd NULL
mov dword ptr ss:[ebp-1Ch], 0h ;WNDCLASSEX.hInstance, dd NULL
mov dword ptr ss:[ebp-18h], ebx ;WNDCLASSEX.hIcon, dd return of LoadIconA
mov dword ptr ss:[ebp-14h], eax ;WNDCLASSEX.hIconSm, dd return of LoadCursorA
mov dword ptr ss:[ebp-10h], 06h ;WNDCLASSEX.hbrBackground, dd COLOR_BTNFACE + 1
mov dword ptr ss:[ebp-0Ch], 0h ;WNDCLASSEX.lpszMenuName, dd NULL
mov dword ptr ss:[ebp-08h], offset WndProc ;WNDCLASSEX.lpszClassName, dd offset of ClassName
mov dword ptr ss:[ebp-04h], ebx ;WNDCLASSEX.hCursor, dd return of LoadIconA
lea eax,[ebp-30h]
push eax
call RegisterClassEx
You just need to put this before the call to CreateWindow.
Any doubt just shout.
PS.: Remember that WndProc is the loop procedure of your Assembly program
.data
wndclass WNDCLASS
.code
push offset wndclass
call RegisterClassEx
You should push its offset, not structure itself
For local variable, push its address
LOCAL wndclass:WNDCLASS
lea edx, wndclass
push edx
call RegisterClassEx
Reverse push the structure to the stack, push the effective address to the first item, call RegisterClassEx, pop the structure off the stack.