I've been trying to make a simple win32 application with NASM for a little while, and after shamelessly ripping the source of another person's work, I have found that it crashes! After cleanly assembling and linking it at the command line:

C:\>nasm -fwin32 hi.asm
C:\>alink -oPE win32.lib -entry _WinMain@16
C:\>hi.exe


and seeing it crash, I loaded it up in the debugger, and found that it simply crashes somewhere right inside of CreateWindowEx

CALL <JMP.&user32.CreateWindowExA> <== Crashes inside this call, never returns 

; Error: Don't know how to continue because 
; memory at address 00000024 is not readable. 
; Try to change EIP or pass exception to program.

TEST EAX,EAX ; Never gets here
JNZ SHORT hi.00401098 ; Never gets here


I do not have many thoughts on why this could have happened. One of my thoughts was that because the WNDCLASS in .DATA section, trying to modify it results in a crash. But, I also don't know how I might declare this locally on the stack, whether this is even needed or not I don't know. The source I used was found in this pack here:  ftp://ftp.szif.hu/pub/demos/tool/gijnwt2.zip and the specific applications source is as follows:

; Example.asm
; ===========
;
; example.asm, a part of the NASM Win32 Coding Toolkit.
; This File demonstrates the use of NASM to make win32 programs.
;
; Not very pretty but it demonstrates some importent points
; about coding with NASM.
;
; Coded By Gij
; If you need to contact me, my E-mail is: gij <at> bigfoot.com

%include "C:\Development\NASM\inc\win32n.inc"

extern RegisterClassA
extern CreateWindowExA
extern CreateWindow
extern ShowWindow
extern UpdateWindow
extern GetMessageA
extern TranslateMessage
extern DispatchMessageA
extern DefWindowProcA
extern ExitProcess
extern MessageBeep
extern GetLastError
extern LoadIconA
extern LoadCursorA
extern PostQuitMessage

extern BeginPaint
extern EndPaint
extern DrawTextA

global _WinMain@16

SEGMENT .text USE32 class=code
_WinMain@16:

%define ebp_hInstance      ebp+8 ; handle of current instance
%define ebp_hPrevInstance  ebp+0ch ; handle of previous instance
%define ebp_lpszCmdLine    ebp+10h ; pointer to command line
%define ebp_nCmdShow        ebp+14h ; show state of window

push ebp
mov ebp,esp

RegisterWindowClass:

mov eax,
mov ,eax

push LPCTSTR IDI_APPLICATION
push HINSTANCE NULL
call LoadIconA

mov ,eax

push LPCTSTR IDC_CROSS
push HINSTANCE NULL
call LoadCursorA

mov ,eax

push dword WindowClassStruc
call RegisterClassA

test eax,eax
jnz .Success

.Fail:
call GetLastError
jmp FareWell

.Success:


MakeWindow:

push LPVOID NULL
push HINSTANCE
push HMENU NULL
push HWND NULL
push INTEGER 50h
push INTEGER 150h
push INTEGER CW_USEDEFAULT  ;
push INTEGER CW_USEDEFAULT  ;
push DWORD WS_OVERLAPPEDWINDOW
push LPCTSTR WindowTitle
push LPCTSTR ClassName
push DWORD WS_EX_OVERLAPPEDWINDOW
        call CreateWindowExA

test eax,eax
jnz .Success

.Fail:
call GetLastError
jmp FareWell

.Success:

mov ,eax

push INTEGER  0xa
push HWND
call ShowWindow

push eax
call UpdateWindow

MsgLoop:

push UINT 0
push UINT 0
push HWND 0
push LPMSG WindowMSG
call GetMessageA

or eax,eax
jz FareWell

push DWORD WindowMSG
call TranslateMessage

push DWORD WindowMSG
call DispatchMessageA

jmp MsgLoop

FareWell:

push UINT 0
call ExitProcess

WndProc:

%define ebp_hWnd  ebp+8 ; handle of window
%define ebp_Msg    ebp+0ch ; message
%define ebp_wParam ebp+10h ; first message parameter
%define ebp_lParam ebp+14h ; second message parameter
%define ebp_DC   ebp-4

push ebp
mov ebp,esp

cmp dword ,WM_DESTROY
jz Destroy_Handler

cmp dword ,WM_PAINT
jz Paint_Handler

.DefMsgHandler:

push dword
push dword
push dword
push dword
call DefWindowProcA
.Exit:

mov esp,ebp
pop ebp
ret 0ch

Destroy_Handler:

push INTEGER 0
call PostQuitMessage
jmp WndProc.Exit

Paint_Handler:

push ebp
mov ebp,esp
sub esp,4

push dword PaintSt
push HWND
call BeginPaint

or eax,eax
jz Paint_Handler.Exit

mov ,eax

push UINT DT_CENTER
push LPRECT RectSt
push INTEGER BOXTEXTLEN
push LPCTSTR BoxText
push HDC
call DrawTextA

push dword PaintSt
push HWND
call EndPaint
.Exit:

mov esp,ebp
pop ebp

jmp WndProc.DefMsgHandler

SEGMENT .data USE32 class=data

WindowTitle db "NASM Win32 Coding Example by Gij",0
ClassName db "NASM_WIN32",0
WindowHandle dd 0

BoxText db "Got Milk?"
BOXTEXTLEN equ $-BoxText

WindowClassStruc:
ISTRUC WNDCLASS
at WNDCLASS.style,            dd    0
at WNDCLASS.lpfnWndProc,      dd    WndProc
at WNDCLASS.cbClsExtra,      dd    0
at WNDCLASS.cbWndExtra,      dd    0
at WNDCLASS.hInstance,        dd    0
at WNDCLASS.hIcon,            dd    NULL
at WNDCLASS.hCursor,          dd    NULL
at WNDCLASS.hbrBackground,    dd    1
at WNDCLASS.lpszMenuName,    dd    NULL
at WNDCLASS.lpszClassName,    dd    ClassName
IEND

WindowMSG:
ISTRUC MSG
at MSG.hwnd,              dd    0
at MSG.message,   dd    0
at MSG.wParam,            dd    0
at MSG.lParam,            dd    0
at MSG.time,              dd    0
at MSG.x,                dd    0
at MSG.y,                dd    0
IEND

PaintSt:
ISTRUC PAINTSTRUCT
IEND

RectSt:
ISTRUC RECT
at RECT.left, dd 0
at RECT.top, dd 0
at RECT.right, dd 150h
at RECT.bottom, dd 50h
IEND


All help is greatly appreciated :D
Posted on 2005-09-10 14:53:41 by Tonto

CreateWindowEx will call your window proc. So I would suggest to set a breakpoint inside WndProc and see if it is reached.
Posted on 2005-09-11 01:11:55 by japheth
Ah, so it does indeed get to my WndProc, but now I find it is crashing somewhere inside DefWindowProc call. I am having trouble debugging it exactly because tracing into the DefWindowProc call just goes through 1,000's of whatever lines of kernel ASM where I see anything going anywhere. An interesting thing I saw said on MSDN, "The WM_NCCREATE message is sent just after your window is created, but if an application responds to this message by returning FALSE, CreateWindowEx function fails. The WM_CREATE message is sent after your window is already created." I thought this was interesting, because that could possibly what my app is doing, but I don't know because DefWindowProc is handling these messages for me. I'm not entirely sure where to go with this problem now, debugging is extremely difficult. As always, thank you for your help!
Posted on 2005-09-11 13:50:23 by Tonto

next step would be to find out at what message it crashes. That should be fairly easy, because there aren't that many message before CreateWindow returns (WM_NCCREATE and WM_CREATE are among them). It would be interesting as well if your WndProc is called just 1 time or at least twice.
Posted on 2005-09-11 14:22:54 by japheth
>> It would be interesting as well if your WndProc is called just 1 time or at least twice.

Yep, it would appear as though it is only hit once.

004010F0  /. 55             PUSH EBP
004010F1  |. 89E5          MOV EBP,ESP
004010F3  |. 817D 0C 020000>CMP DWORD PTR SS:,2
004010FA  |. 74 20          JE SHORT hi.0040111C
004010FC  |. 817D 0C 0F0000>CMP DWORD PTR SS:,0F
00401103  |. 74 23          JE SHORT hi.00401128
00401105  |> FF75 14        PUSH DWORD PTR SS:              ; /lParam
00401108  |. FF75 10        PUSH DWORD PTR SS:              ; |wParam
0040110B  |. FF75 0C        PUSH DWORD PTR SS:                ; |Message
0040110E  |. FF75 08        PUSH DWORD PTR SS:                ; |hWnd
00401111  |. E8 2C1F0000    CALL <JMP.&user32.DefWindowProcA>        ; \DefWindowProcA

  ... etc etc with the WinProc, WM_PAINT and WM_DESTROY handling ...


I have a break on that PUSH EBP entry to the WinProc and it is only hit once, and then it goes into DefWindowProc and dies "somewhere". Now the message that is first sent is a WM_GETMINMAXINFO message. The stack right at the call:

0012F890   00401116  /CALL to DefWindowProcA from hi.00401111
0012F894  001702D0  |hWnd = 001702D0 (class='NASM_WIN32')
0012F898  00000024  |Message = WM_GETMINMAXINFO
0012F89C  00000000  |wParam = 0
0012F8A0  0012F9D0  \pMinMax = 0012F9D0


From there, it jumps around in kernel32 code, and crashes somewhere, on this same DefWindowProc call. It's pretty confusing to me ;) and I don't know whereabotus in my debugging adventures I might go next. But again, thanks for your help!
Posted on 2005-09-11 17:59:20 by Tonto

One strategy could be to "disable" calling the default window handler for WM_getminmaxinfo. this would at least show if it is this message which causes trouble or if your wndproc has some other problems so it is never called more than once.

"disable" means that you should handle the message in your wndproc. I don't have the docs available, but just returning 0 is in most cases valid.
Posted on 2005-09-11 19:05:03 by japheth
Very well then, our good freinds at MSDN say this about the WM_GETMINMAXINFO:
Return Value: If an application processes this message, it should return zero.


So I did:

cmp dword ,WM_GETMINMAXINFO
jz Minmax_Handler

... etc ...

Minmax_Handler:
mov esp, ebp
pop ebp
ret


and that worked fine. Then we get up to our WM_NCCREATE message:

0012F854   0040111F  /CALL to DefWindowProcA from hi.0040111A
0012F858  000C0472  |hWnd = 000C0472 (class='NASM_WIN32')
0012F85C  00000081  |Message = WM_NCCREATE
0012F860  00000000  |wParam = 0
0012F864  0012F9B8  \pCreate = 0012F9B8


And it crashes. What odd behavior! I did the same thing for the WM_NCCREATE messge:

Nccreate_Handler:
mov esp, ebp
pop ebp
mov eax, 1
ret


And then that works fine. And then we come around for a third DefWindowProc handle with a WM_NCCALCSIZE message:

0012F8A8   00401128  /CALL to DefWindowProcA from hi.00401123
0012F8AC  00260318  |hWnd = 00260318 (class='NASM_WIN32')
0012F8B0  00000083  |Message = WM_NCCALCSIZE
0012F8B4  00000000  |CalcFlag = FALSE
0012F8B8  0012F9F0  \Data = 0012F9F0


And it crashes. There seems to be something absolutely catastrophic with this call that is very confusing. I am push'ing the arguments properly right? Is there anything I might be missing? Oh woe is me, I am so distraught ;) Again, thanks for yoru continued support ;)
Posted on 2005-09-11 20:50:04 by Tonto
Why do you have 2 .Exit?

If you process the message, you do not need to call DefWindowProc, so your last .Exit is wrong. Instead it should be replaced with jmp .Exit
Posted on 2005-09-11 20:59:50 by roticv
Oh my dull head can't take that.

>> Instead it should be replaced with jmp .Exit

Of course I was just copying the code I was given from this tool pack. So, I see the second .Exit is referenced:

	call BeginPaint

or eax,eax
jz Paint_Handler.Exit

  ...

; This is Paint_Handler.Exit
.Exit:

mov esp,ebp
pop ebp

jmp WndProc.DefMsgHandler


Which would I guess exit if that BeginPaint failed (returned 0). I guess you are saying that I should not have that .Exit there, and that I should merely jmp WndProc.Exit right then and there? I guess that would make sense, as you shouldn't need to DefWindowProc if yoru WM_PAINT handling failed. My code, I presume would then look something like:

	call BeginPaint

or eax,eax
jz WndProc.Exit


Well, it's certainly more logical now :D but it's still crashing, for some very very odd reason at DefWindowProc call every time. Oh well, thanks for your continued support.
Posted on 2005-09-11 21:24:26 by Tonto

push HWND
call ShowWindow
push eax
call UpdateWindow

is clearly wrong, pushes should be swapped. (btw, UpdateWindow after ShowWindow is an artifact from Win3.1, useless  in 95/NT). Also, calling DefWindowProc after WM_PAINT should not be done.

At least for now, try not linking with lcclink, lots of extra code is a no-no for debugging (unless you use VKDebug-like methods).

in your Paint_handler, you forget to adjust the stack, if I'm not wrong (here it's 6:33am now, all-nighter....) .

I need to bookmark this code to stuff it in the face of ".if/.elseif" haters :P

P.S:
Also, checking for impossible errors like not-registered wndclasses, not created windows, invalid eax after BeginPaint ... are a waste of time. If any of them ever happens, then the OS is currently in a terrible condition - and you will never be to blame. You'd better save time for checking on things that could actually get wrong. But sure, a bit of training is good, too.
Posted on 2005-09-11 22:39:19 by Ultrano

I need to bookmark this code to stuff it in the face of ".if/.elseif" haters

It's more of a thing to shove in the throaths of invoke+proc haters... :)
Posted on 2005-09-11 22:49:28 by f0dder
	push dword PaintSt
push HWND
call EndPaint
.Exit:

mov esp,ebp
pop ebp

jmp WndProc.DefMsgHandler


This is wrong. You are unwinding the stack one too many times. (Notice you have the POP EBP in the DefMsgHandler code.) As stated by others, there is no need to call DefWindowProc after WM_PAINT has been handled.

	push dword PaintSt
push HWND
call EndPaint
.Exit:

mov esp,ebp
pop ebp

ret 0ch

Posted on 2005-09-11 23:18:28 by tenkey
Ah! Somewhere along the lines (actually, the exact one was changing ret 0ch to simply ret) it stopped crashing! Well that is infinitely nifty ;)

WndProc:

%define ebp_hWnd  ebp+8 ; handle of window
%define ebp_Msg    ebp+0ch ; message
%define ebp_wParam ebp+10h ; first message parameter
%define ebp_lParam ebp+14h ; second message parameter

push ebp
mov ebp,esp

cmp dword , WM_DESTROY
jz Destroy_Handler


.DefMsgHandler:

push dword
push dword
push dword
push dword
call DefWindowProcA
.Exit:

mov esp,ebp
pop ebp
ret

Destroy_Handler:

push INTEGER 0
call PostQuitMessage
jmp WndProc.Exit


Well, now my alternate problem. When it first opens, it is minimized and when I click it there's no window. Perhaps this is like the curse mentioned by Ultrano. I changed my code to:

	mov ,eax
push INTEGER SW_SHOW
push HWND eax
call ShowWindow


Leaving out the UpdateWindow call (who cares about 95), throwing in SW_SHOW instead of whatever 0xa was. So now, it starts minimized and I can't get it to maximize even when I click it. A bit odd. Now this is merely a PEBKAC problem between me and the WINAPI, and me being too stupid too track down the problem. Maybe I'll find it at some point, if it's glaringly obvious maybe someone can shout it out at me. Thanks for the help, I'm much clsoer than before!
Posted on 2005-09-11 23:54:24 by Tonto