Hey everyone,

I am trying to do a simple window program in assembly. Essentially I am following the winprog.net tutorial (http://winprog.net/tutorial/simple_window.html) but writing assembly instead of C. I am also using the NASM32 package.

Here is the code I have:


%include '\nasm32\inc\win32\windows.inc'
%include '\nasm32\inc\win32\kernel32.inc'
%include '\nasm32\inc\win32\user32.inc'
%include '\nasm32\inc\nasm32.inc'

segment .data
szClassName db "window_class",0

wc:
    istruc WNDCLASSEX
        at WNDCLASSEX.cbSize,          dd    WNDCLASSEX_size
        at WNDCLASSEX.style,          dd    NULL
        at WNDCLASSEX.lpfnWndProc,    dd    NULL
        at WNDCLASSEX.cbClsExtra,      dd    NULL
        at WNDCLASSEX.cbWndExtra,      dd    NULL
        at WNDCLASSEX.hInstance,      dd    NULL
        at WNDCLASSEX.hIcon,          dd    NULL
        at WNDCLASSEX.hCursor,        dd    NULL
        at WNDCLASSEX.hbrBackground,  dd    COLOR_WINDOW+1
        at WNDCLASSEX.lpszMenuName,    dd    NULL
        at WNDCLASSEX.lpszClassName,  dd    NULL
        at WNDCLASSEX.hIconSm,        dd    NULL
    iend

message:
    istruc MSG
        at MSG.hwnd,                  dd    NULL
        at MSG.message,                dd    NULL
        at MSG.wParam,                dd    NULL
        at MSG.lParam,                dd    NULL
        at MSG.time,                  dd    NULL
        at MSG.pt,                    dd    NULL
    iend

regFailCaption db "Registration Error!",0
regFailText db "Registration of Window Class failed!",0

createFailCap db "Window Error!",0
createFailTxt db "Creation of Window Failed!",0

windowTitle db "Window Title",0


segment .bss
hWnd resd 1
hInstance resd 1

%define GetModuleHandleA _GetModuleHandleA@4
%define ExitProcess _ExitProcess@4
%define LoadIconA _LoadIconA@8
%define LoadCursorA _LoadCursorA@8
%define RegisterClassExA _RegisterClassExA@4
%define CreateWindowExA _CreateWindowExA@48
%define ShowWindow _ShowWindow@8
%define UpdateWindow _UpdateWindow@4
%define GetMessageA _GetMessageA@16
%define TranslateMessage _TranslateMessage@4
%define DispatchMessageA _DispatchMessageA@4
%define MessageBoxA _MessageBoxA@16
%define DefWindowProcA _DefWindowProcA@16
%define DestroyWindow _DestroyWindow@4
%define PostQuitMessage _PostQuitMessage@4

segment .text
extern GetModuleHandleA
extern ExitProcess
extern  LoadIconA
extern LoadCursorA
extern RegisterClassExA
extern CreateWindowExA
extern ShowWindow
extern UpdateWindow
extern  GetMessageA
extern TranslateMessage
extern  DispatchMessageA
extern  MessageBoxA
extern  DefWindowProcA
extern DestroyWindow
extern  PostQuitMessage

global WinMainCRTStartup


WinMainCRTStartup:
push NULL
call GetModuleHandleA
mov , eax

push SW_SHOWNORMAL
push NULL
push NULL
push dword
call WinMain

push NULL
call ExitProcess

ret


%define hinst
%define hpinst 
%define cmdl
%define cmds
WinMain:
push ebp
mov ebp, esp

mov dword , WndProc

mov eax, hinst
mov , eax

push IDI_APPLICATION
push NULL
call LoadIconA

mov dword , eax
mov dword , eax

push IDC_ARROW
push NULL
call LoadCursorA

mov dword , eax
mov dword , szClassName

push wc
call RegisterClassExA
cmp eax, 0
jz near regfail

push NULL
push dword
push NULL
push NULL
push 120
push 240
push CW_USEDEFAULT
push CW_USEDEFAULT
push WS_OVERLAPPEDWINDOW
push windowTitle
push szClassName
push WS_EX_CLIENTEDGE
call CreateWindowExA

cmp eax, 0
jz near winfail

mov , eax

push dword cmds
push dword
call ShowWindow

push dword
call UpdateWindow

msgLoop:
push 0
push 0
push NULL
push message
call GetMessageA

cmp eax, 0
jle near end_WinMain

push message
call TranslateMessage

push message
call DispatchMessageA

jmp msgLoop

jmp end_WinMain

regfail:
mov eax, MB_OK
or eax, MB_ICONEXCLAMATION

push eax
push regFailCaption
push regFailText
push NULL
call MessageBoxA

jmp end_WinMain

winfail:
mov eax, MB_OK
or eax, MB_ICONEXCLAMATION

push eax
push createFailCap
push createFailTxt
push NULL
call MessageBoxA

jmp end_WinMain

end_WinMain:
mov dword eax,

mov esp, ebp
pop ebp
ret

%define hwnd
%define msg
%define wparam
%define lparam
WndProc:
push ebp
mov ebp, esp

cmp dword msg, WM_CLOSE
jz wm_close
cmp dword msg, WM_DESTROY
jz wm_destroy

push dword lparam
push dword wparam
push dword msg
push dword hwnd
call DefWindowProcA

ret

wm_close:
push dword hwnd
call DestroyWindow
jmp end_WndProc

wm_destroy:
push 0
call PostQuitMessage

end_WndProc:
xor eax, eax
mov esp, ebp
pop ebp
ret


Compile with:


nasm -f win32 window.asm -o window.obj
link.exe /subsystem:windows /LIBPATH:\nasm\lib\win32 window.obj kernel32.lib user32.lib


My problem is that the code does not work. Running it will give a "window.exe has encountered an error and needs to close" error message. If I run the program in OllyDBG I can see that CreateWindowExA is producing an exception. I don't see why this is happening or how I can fix it.

Also, is there a way I can avoid having to do all those %define's and extern's WITHOUT USING invoke. I don't like invoke.

Thanks in advance.
Posted on 2007-03-19 23:38:55 by lithium

Compile with:


It's an *assembler*, not a "compiler" ;)



nasm -f win32 window.asm -o window.obj
link.exe /subsystem:windows /LIBPATH:\nasm\lib\win32 window.obj kernel32.lib user32.lib



Well, I have the latest NASM32 package, so I'd be impressed if this would assemble at all.


My problem is that the code does not work. Running it will give a "window.exe has encountered an error and needs to close" error message. If I run the program in OllyDBG I can see that CreateWindowExA is producing an exception. I don't see why this is happening or how I can fix it.

Also, is there a way I can avoid having to do all those %define's and extern's WITHOUT USING invoke. I don't like invoke.

Thanks in advance.


First, NASM32 expects an "entry" identifier pointing to a procedure, like so...


entry WinMainCRTStartup

proc WinMainCRTStartup
;code...
endproc


NASM32 removes dependency on the linker to resolve the "default entry symbol" by creating a uniform method. "entry XXX" maps the target procedure as the global procedure "main" so that the linker knows which procedure is the entry-point every single time.

I've thought about overloading CALL a long time ago, but that would create too much confusion and incompatibility. Hence the use of the INVOKE macro, as it knows how to deal with all the include files. CALL is reserved for legacy use, that which requires thorough design by the user.

Also, INVOKE knows what kind of CALL is being made between STDCALL and CDECL and automatically fills in the rest and cleans things up so the user doesn't have to.

To summarize, you are not using NASM32 as it was intended to be used. It seems like you want to use strictly NASM, and in this respect, the NASM32 include files will not serve any purpose for you. I would suggest utilizing Google to find out how to do what you are trying to do, as that process has been documented enough times.

However, if you do wish to use NASM32 as it was intended to be used, there are enough DEMO examples of how to create a basic Window application. See the README.TXT file in the "nasm32\demos\win32\" directory for more information.

NOW, to add a little philosophy. I can almost guarantee that with enough time, lithium, that you will grow tired of writing endless API calls, line after line of PUSH with a final CALL. The beauty of INVOKE is that it eases the "pain" of the constant calling of *uniform* APIs without sacrificing efficiency. INVOKE also protects you from overlooking if you need to clean-up the stack or not. INVOKE is not only limited to API calls, however, is it knows how to traverse PROC/ENDPROC procedures.

So, in either case, I hope you now have the information you need to move forward. Good luck :)
Posted on 2007-03-20 00:19:46 by SpooK
Thanks for the reply SpooK.

I realize that I am not using NASM32 (I also have the latest version) the way it was intended to be used. However, I did try to use it this way. I took a look at all the example code (DEMO2 is very similar to what I am trying to do) and I tried doing some code this way. The fact is, I didn't like it very much. It feels more more like a HLL than assembly code to me. What I like about coding in assembly is that I can write very many short statements that the processor executes. I know this probably sounds idiotic but using INVOKE just doesn't feel good for me. I am more bothered by the fact that I have a 120 character wide line for a long function call (such as a call to CreateWindowExA) than I am by having to "push" 12 times or that I may have to look up whether a function uses cdecl or stdcall. Again, I know that this probably sounds stupid but I'm not saying that INVOKE is not "real" assembly, I'm just saying that I don't like how this stuff looks.

What I really want from NASM32 are the constants. I would really rather not have to look up what WM_CLOSE, MB_OK, etc... really are. The same goes for all the structures (such as WNDCLASSEX and MSG). Essentially, the stuff found in windows.inc is what I want. I would also like a giant file full of lines like "%define _function@X function" so that I could just call function instead of _function@X. And if there was a way to not have to write all of those externs, that would be wonderful. But that's all that I want.

Having said all that, I don't think that is the problem. I compiled the code using NASM (not NASM32) and it still crashes after CreateWindowExA. Any ideas about this?

Thanks again.

Posted on 2007-03-20 01:30:51 by lithium

Thanks for the reply SpooK.

I realize that I am not using NASM32 (I also have the latest version) the way it was intended to be used. However, I did try to use it this way. I took a look at all the example code (DEMO2 is very similar to what I am trying to do) and I tried doing some code this way. The fact is, I didn't like it very much. It feels more more like a HLL than assembly code to me.


Yes... and Windows is an OS with an API that is designed around an HLL. When in Rome...


Having said all that, I don't think that is the problem. I compiled the code using NASM (not NASM32) and it still crashes after CreateWindowExA. Any ideas about this?

Thanks again.


Yes, the same thing that INVOKE is supposed to help prevent... you are not cleaning up the stack after each STDCALL... so I wouldn't be surprised if your program is crashing from stack corruption.

I will give you a fix for one of your calls, LoadIconA, that doesn't require POPs and can apply to the rest of your source.


push IDI_APPLICATION
push NULL
call LoadIconA
add esp,8  ;Compensate for two DWORD pushes onto the stack instead of 2 useless POPs


For every DWORD push prior to a STDCALL, simply add 4 to ESP. Good luck ;)
Posted on 2007-03-20 01:53:16 by SpooK
Here are your fixes:

1. in WinMain and WndProc change ret to ret 16
2. after DefWindowProcA do not "ret", but jump to end_WndProc after xor eax,eax.

Have a look on other programmng style (nasm too):
Attachments:
Posted on 2007-03-20 04:42:12 by sapero

I will give you a fix for one of your calls, LoadIconA, that doesn't require POPs and can apply to the rest of your source.


push IDI_APPLICATION
push NULL
call LoadIconA
add esp,8  ;Compensate for two DWORD pushes onto the stack instead of 2 useless POPs


For every DWORD push prior to a STDCALL, simply add 4 to ESP. Good luck ;)


Umm....you're not supposed to do that with stdcall. With stdcall, the callee pops the arguments from the stack. I think you're thinking of cdecl. Or am I thoroughly confused here?

Anyway, I did what sapero suggested and now it works. What doesn't make sense to me is why CreateWindowEx would fail if WinMain and WndProc return incorrectly. Both of those return AFTER CreateWindowEx is called.

I've attached my code. Anyway, I PERSONALLY think that my code is much cleaner than, for example, DEMO2. I realize that most people probably disagree and that my method has an obvious disadvantage of not using INVOKE but if I were to write DEMO2 like it is provided in NASM32, I would feel better just doing it in C. And my whole purpose in doing this was to try Win32 with assembly. Again, I'm not saying you're wrong, SpooK. In fact, you're almost certainly right. I'm just saying that if I were programming something in assembly, I would like how my code looks a lot more (lack of comments aside...)

Thanks again for all the help everyone.
Attachments:
Posted on 2007-03-20 15:20:43 by lithium

Umm....you're not supposed to do that with stdcall. With stdcall, the callee pops the arguments from the stack. I think you're thinking of cdecl. Or am I thoroughly confused here?


Yeah... sorry... you're correct... otherwise C vararg wouldn't work :shock:

No more posts when I'm tired :|

Anyhow, you are taking the right route if you really want to learn how things work "behind the scenes" ;)
Posted on 2007-03-20 15:53:37 by SpooK
Btw, In regards to this post. If you wanted to use 'CALL' with the procedure includes you could subclass the  CALL opcode with a macro like we did with leave and ret.

%imacro CALL 1
%push invoke

%ifdef __stdcall_defined_%1
%define %1 __stdcall_defined_%1
extern __stdcall_defined_%1
%endif

%ifdef __cdecl_defined_%1
declare_cdecl %1
%define %1 __cdecl_defined_%1
extern __cdecl_defined_%1
declare_cdecl %1
%endif

%ifdef _UNDERSCORE_
%define _proc _%1
%else
%define _proc %1
%endif

call _proc
%pop
%endmacro


This would allow you to use CALL just like you normally would, except you can use the Kernel32.inc, User32.inc, etc from the Nasm32 project without having to define the procedures yourself. I personally use the "low level" syntax myself, but I exclusively use Nasm32 when working with the Netwide Assembler (although I am a bit partial). The downside to doing this is that you have to cleanup the stack yourself, I don't mind and I'm sure you don't either. Another thing I commonly do to save some typing is to create type shortcuts:

%define T tword
%define Q qword
%define D dword
%define W word
%define B byte


Using these two will allow you to cut down your code, with use of the NASM32 project, to look more like:

push D MB_YESNO
push D szTitle
push D szMessage
push D NULL
call MessageBoxA
mov B , al


Just these few macros added to the NASM32 project can save your fingers a whole lot, while still allowing you to work in the low level style you are comfortable with. Sorry that I didn't get to this topic sooner, I've been a little busy lately on other things. I hope this has been of some help to ya.

Regards,
Bryant Keller

Posted on 2007-03-20 19:14:03 by Synfire
I was saving this for the next release... but...


Btw, In regards to this post. If you wanted to use 'CALL' with the procedure includes you could subclass the  CALL opcode with a macro like we did with leave and ret.

%imacro CALL 1
%push invoke

%ifdef __stdcall_defined_%1
%define %1 __stdcall_defined_%1
extern __stdcall_defined_%1
%endif

%ifdef __cdecl_defined_%1
declare_cdecl %1
%define %1 __cdecl_defined_%1
extern __cdecl_defined_%1
declare_cdecl %1
%endif

%ifdef _UNDERSCORE_
%define _proc _%1
%else
%define _proc %1
%endif

call _proc
%pop
%endmacro


This would allow you to use CALL just like you normally would, except you can use the Kernel32.inc, User32.inc, etc from the Nasm32 project without having to define the procedures yourself. I personally use the "low level" syntax myself, but I exclusively use Nasm32 when working with the Netwide Assembler (although I am a bit partial). The downside to doing this is that you have to cleanup the stack yourself, I don't mind and I'm sure you don't either. Another thing I commonly do to save some typing is to create type shortcuts:


The latest version of NASM32 will be the last version available with a NASM32.INC file such as described above. I've changed the entire process and explicit defines for external function imports have been removed. Instead, the NASM32.INC file appropriately parses the include files with an overloaded EXPORT macro.


;STDCALL
EXPORT CreateDirectoryExA, 12 ; _CreateDirectoryExA@12

;CDECL
EXPORT printf ; _printf


The previously established use of INVOKE will not be affected by this change.

Also, based upon an earlier idea that has been recently re-suggested, I will decorate default API calls. For example, CreateDirectoryEx would resolve as CreateDirectoryExA OR CreateDirectoryExW depending on user selection of an ASCII/Wide(Unicode) switch. The switch (a simple %define) will need to be one or the other, otherwise NASM32 defaults strictly to what each EXPORT defines.

However, I am currently busy with NASM itself, as I am doing some heavy rework to support x64 output. So I will release more information when it is available (i.e. I work on it again.) ;)
Posted on 2007-03-20 21:56:47 by SpooK
Bryant,

That is perfect. Exactly what I was looking for. Thank you.
Posted on 2007-03-20 22:14:16 by lithium
I was saving this for the next release... but...

...redundant quote...


The latest version of NASM32 will be the last version available with a NASM32.INC file such as described above. I've changed the entire process and explicit defines for external function imports have been removed. Instead, the NASM32.INC file appropriately parses the include files with an overloaded EXPORT macro.


;STDCALL
EXPORT CreateDirectoryExA, 12 ; _CreateDirectoryExA@12

;CDECL
EXPORT printf ; _printf


This merely for within the scope of the procedure includes, correct. I've not tested this at all, but theoretically I'm not sure if this would work. EXPORT is a p-op, those are parsed before macros, unless nested within one, in NASM so they can't be overloaded. I'm not 100% sure on this, I might be wrong as I don't have a copy of NASM here to test it and I'm just kinda passing through. But iirc when I tried to overload %include to support variables in the path name such as $windir$ and $progfiles$ I kept getting shot down by precedence. It'll be something to keep an eye out for.

The previously established use of INVOKE will not be affected by this change.


True, but it would make the procedural include files much cleaner to look at. Btw, you might want to give Vortex a yell when you implement this, he will probably want to update his Nasm32 scanner tool thing.

Also, based upon an earlier idea that has been recently re-suggested, I will decorate default API calls. For example, CreateDirectoryEx would resolve as CreateDirectoryExA OR CreateDirectoryExW depending on user selection of an ASCII/Wide(Unicode) switch. The switch (a simple %define) will need to be one or the other, otherwise NASM32 defaults strictly to what each EXPORT defines.

However, I am currently busy with NASM itself, as I am doing some heavy rework to support x64 output. So I will release more information when it is available (i.e. I work on it again.) ;)


Honestly, with the latest 03/08/2007 update of NASM32, there is no real need to stress it right now. I would much rather see a new version of NASM released. But then again that's just my opinion. :p


Bryant,

That is perfect. Exactly what I was looking for. Thank you.


No worries mate. Just glad I could help.
Posted on 2007-03-21 18:47:40 by Synfire

This merely for within the scope of the procedure includes, correct. I've not tested this at all, but theoretically I'm not sure if this would work. EXPORT is a p-op...


I suppose there are other options...

API_CALL
API_EXPORT
LIB_EXPORT
etc...

Let me know what you think ;)
Posted on 2007-03-21 19:55:10 by SpooK