When I compile and link, I don't get any errors.. running the program from command prompt causes it to drop to a blank line like the program is running, but absolutely nothing happens. I'm obviously missing something small, or I've done something stupid (or both). Or perhaps I've made a very large oversight.. I don't know :(
This is just a basic window program.. as I understand the code, it should do nothing but open a blank window. Code is taken almost line for line from a tutorial - I replaced all of the invoke commands with call commands (pushing the parameters myself), but other than that it is line for line.
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
.data
AppName db "AppName",0
ClassName db "ClassName",0
var db 1
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.code
start:
push NULL
call GetModuleHandle
mov hInstance,eax
call GetCommandLine
mov CommandLine,eax
push SW_SHOWDEFAULT
push NULL
push NULL
lea eax,hInstance
push eax
call WinMain
push NULL
call ExitProcess
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASS
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground, COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName, OFFSET ClassName
push IDI_APPLICATION
push NULL
call LoadIcon
mov wc.hIcon,eax
mov wc.hIconSm,eax
push IDC_ARROW
push NULL
call LoadCursor
mov wc.hCursor,eax
lea eax,wc
push eax
call RegisterClassEx
push NULL
push hInst
push NULL
push NULL
push CW_USEDEFAULT
push CW_USEDEFAULT
push CW_USEDEFAULT
push CW_USEDEFAULT
push WS_OVERLAPPEDWINDOW
lea eax,AppName
push eax
lea eax,ClassName
push eax
push NULL
call CreateWindowEx
mov hwnd,eax
push CmdShow
push hwnd
call ShowWindow
push hwnd
call UpdateWindow
.WHILE TRUE
push 0
push 0
push NULL
push msg
call GetMessage
; .BREAK .IF(!eax)
lea eax,msg
push eax
call TranslateMessage
lea eax,msg
push eax
call DispatchMessage
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
push NULL
call PostQuitMessage
.ELSE
push lParam
push wParam
push uMsg
push hWnd
call DefWindowProc
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
I've tried replacing the "call" calls with "invoke" and setting the parameters the same as in the tutorial.. same effect.
mov wc.cbSize,SIZEOF WNDCLASS
mov wc.cbSize, SIZEOF WNDCLASSEX
To get it working..
push SW_SHOWDEFAULT
push NULL
push NULL
;;lea gives you the address of a variable, not its value
; lea eax,hInstance
mov eax,hInstance
push eax
call WinMain
;;you're calling RegisterClassEx, so you need to use the appropriate 'ex' structure
;;it's generally safer to use the size of the structure you're actually using (i.e. 'wc')
; mov wc.cbSize,SIZEOF WNDCLASS
mov wc.cbSize,SIZEOF wc
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
push 0
push 0
push NULL
;;this tries to push the value of msg, when you should be giving its address
; push msg
lea eax,msg
push eax
call GetMessage
..uncomment the .BREAK .IF(!eax) so your program can close.
Additional little 'fixes..'
push SW_SHOWDEFAULT
push CommandLine
push NULL
mov eax,hInstance
push eax
call WinMain
push hInst
pop wc.hInstance
Thank you both (Especially you Tedd) for your help. Upon making the corrections, the program loads and runs as expected.
After reviewing these changes and comparing them to the Iczelion tutorial, I noticed that in a few places where he used invoke, several parameters had ADDR in front of the variable and several places did not -- I suspect that was probably my biggest mistake overall (my using or not using LEA when pushing values before calling).
Thanks again guys,
a little tip :
lea eax, hInstance
push eax
can be converted to 'push hInstance' instead
similarly, instead of lea for pushing pointers to a string you can just do 'push offset AppName'
Quote from: Slugsnack on August 31, 2009, 02:44:20 PM
a little tip :
lea eax, hInstance
push eax
can be converted to 'push hInstance' instead
similarly, instead of lea for pushing pointers to a string you can just do 'push offset AppName'
Your first example would certainly depend on which assembler you may be using. With MASM,
push hInstance would push the actual content of the variable named hInstance (equivalent to the
mov eax,hInstance / push eax sequence) and NOT its memory address (obtained with
lea eax,hInstance).
hiyas Mike and Ray,
shouldn't this work on most assemblers ???
push ADDR hInstance
i suppose it would generate the lea, huh
next question: isn't the lea going to be neccessary for locals, either way ???
I wanted to avoid too much confusion, so just stuck with LEA for the variables (since there were already enough problems for a beginner.)
LEA is necessary for referencing local variables (and parameters) because you can't get the actual address before run-time, since they're referenced as an offset from ebp/esp. Unlike global variables, where their address is known in advance.
If you just want to push the value, then there's no need for the lea, as you can push it directly (as Slugsnack said)
(For MASM) ADDR only works in invoke (where it adds LEA if necessary), you need to use OFFSET anywhere else.
I made the previous comment because some other assembler(s) (I think NASM is one of them) treat for example:
mov eax,hInstance as load eax with the address of the variable hInstance, and
mov eax,[hInstance] as load eax with the value of the variable.
And thus similarly, push hInstance in such assembler(s) would mean to push its address.
thanks Tedd and Ray
for me, i try to write the code out as it will be assembled - lol
that way, when i look at it with a debugger, i get no surprises
plus, it helps me to learn, i think