I decided to work with the petzold book ( ::) yes, you can say i'm an old fashion dude :bgrin:) to enter more deeply in the windows gui programming. :exclaim: But as a close reference to the win api functions or windows related topics, no intention to imitate the c programs in the book :badgrin:.
Here it is the first program based on the first example of that book (.exe attached):
**Wait! the program is in that way writed just for clarity, no big intention i did to do an optimized program** :redface:
Ok, now have a look:
:biggrin:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Started by Felipe the 2017-10-29
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
.data
align 1
widbuff byte 4 dup(' ')
heibuff byte 4 dup(' ')
errotit byte 'ERROR DETECTED',0
msgerro byte 'There was an error with the program.',0ah,0dh,\
'You can try to use a debugger to watch it.',0
scretit byte 'SCREEN SIZE IN PIXELS',0
scremsg byte 'WIDTH: ',4 dup(' '),0ah,0dh,'HEIGHT: ',4 dup(' '),0
.data?
align 4
widthx dword ?
heighty dword ?
.code
align 4
start:
push SM_CXSCREEN ; Get the screeen width in pixels.
call GetSystemMetrics
cmp eax,0
je erro ; In case of error show a message and exit.
mov widthx,eax ; Store the width here.
push SM_CYSCREEN ; Get the screen height in pixels.
call GetSystemMetrics
cmp eax,0
je erro ; In case of error show a message and exit.
mov heighty,eax ; Store the height here.
mov eax,widthx ; Lets process the width first.
mov esi,offset widbuff+3 ; From the less significant digit.
mov ecx,10 ; We divide by 10 to convert the binary
; into unpacked bcd.
align 4
getbcd:
xor edx,edx ; Cleaning the unpacked bcd temporal storage.
div ecx ; Dividing the width number by 10.
mov [esi],dl ; Moving to the width buffer the less significant digit (u_bcd).
dec esi ; Next digit (from right to left).
cmp eax,ecx ; If the dividend is greater or equal
jae getbcd ; to 10 we continue.
mov [esi],al ; Moving the most significand digit into the width buffer.
mov esi,offset widbuff ; From the most significand digit now.
mov edx,4 ; 4 digits cycle.
align 4
getascii:
or byte ptr[esi],30h ; Making an unpacked bcd an ascii number.
inc esi ; Next one (from left to right).
dec edx ; Decrement the counter.
jnz getascii
mov eax,heighty ; Lets process now the height.
mov esi,offset heibuff+3 ; From the less significant digit.
mov ecx,10 ; We divivde by 10 to convert the binary
; into unpacked bcd.
align 4
getbcd2:
xor edx,edx ; Cleaning the unpacked bcd temporal storage.
div ecx ; Dividing the height number by 10.
mov [esi],dl ; Moving to the height buffer the less significant digit (u_bcd).
dec esi ; Next digit (from right to left).
cmp eax,ecx ; If the dividend is greater or equal
jae getbcd2 ; to 10 we continue.
mov [esi],al ; Moving the most significand digit into the height buffer.
mov esi,offset heibuff ; From the most significand digit now.
mov edx,4 ; 4 digits cycle.
align 4
getascii2:
or byte ptr[esi],30h ; Making an unpacked bcd an ascii number.
inc esi ; Next one (from left to right).
dec edx ; Decrement the counter.
jnz getascii2
mov esi,offset widbuff ; The most significand digit.
cmp byte ptr[esi],30h ; Is a zero? (like in 800x600: only 3 digits for the width).
jne heispace ; If not we now check the height string.
sub byte ptr[esi],10h ; Convert that 30h (ascii zero number) into a 20h (ascii space).
align 4
heispace:
mov esi,offset heibuff ; The most significand digit.
cmp byte ptr[esi],30h ; Is a zero? (like in 800x600: only 3 digits for the height).
jne movvar ; If not we are almost ready to write the string.
sub byte ptr[esi],10h ; Convert that 30h (ascii zero number) into a 20h (ascii space).
align 4
movvar:
mov edi,offset scremsg+7 ; We will put here (in the message box)
mov esi,offset widbuff ; what's here.
movsd
mov edi,offset scremsg+21 ; We will put here (in the message box)
mov esi,offset heibuff ; what's here.
movsd
push MB_OK ; Just one button.
push offset scretit
push offset scremsg
push NULL
call MessageBox ; Show the screen size in pixels.
jmp normal ; Finish the program.
align 4
erro:
push MB_OK
push offset errotit
push offset msgerro
push NULL
call MessageBox
align 4
normal:
push 0
call ExitProcess
end start
And here it is the next program (based in the next example of the book) The .exe is attached:
:idea:
; ¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤
; This program was started by Felipe in 2017-11-03.
; ¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\gdi32.lib
.data
align 1
wndclaname byte "EXAMPLE WINDOW CLASS",0 ; Name of the window class.
wndname byte "WINDOW EXAMPLE",0 ; Title bar of the window.
msghello byte "Well, here it is a way of how to do a window in Windows. ",\
"There are many others by the way.",0 ; Message drawed in the window client area.
.data?
align 4
insthan dword ? ; Instance handle.
wndclaex dword 12 dup(?) ; WNDCLASSEX structure.
wndhand dword ? ; Window handle.
msgstru dword 7 dup(?) ; MSG structure.
hdc dword ?
painstru dword 12 dup(?) ; PAINTSTRUCT structure.
rect dword 4 dup(?) ; RECT structure.
.code
align 4
start:
push NULL
call GetModuleHandle
mov insthan,eax
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Fill the WNDCLASSEX structure:
mov dword ptr wndclaex[0],sizeof wndclaex ; Size of structure.
mov dword ptr wndclaex[4],CS_HREDRAW or CS_VREDRAW ; Style of class.
mov wndclaex[8],wndproc ; Address of wndproc (callback proc).
mov dword ptr wndclaex[12],NULL
mov dword ptr wndclaex[16],NULL
mov eax,insthan
mov wndclaex[20],eax
push 500
push insthan
call LoadIcon
mov wndclaex[24],eax
push IDC_ARROW
push NULL
call LoadCursor
mov wndclaex[28],eax
push WHITE_BRUSH
call GetStockObject
mov wndclaex[32],eax
mov dword ptr wndclaex[36],NULL
mov wndclaex[40],offset wndclaname ; Address of the class name.
mov dword ptr wndclaex[44],NULL
push offset wndclaex ; Address of the WNDCLASSEX structure.
call RegisterClassEx
push NULL
push insthan
push NULL
push NULL
push CW_USEDEFAULT
push CW_USEDEFAULT
push CW_USEDEFAULT
push CW_USEDEFAULT
push WS_OVERLAPPEDWINDOW
push offset wndname ; Address of the string displayed in the title bar.
push offset wndclaname ; Address of the WNDCLASSEX structure.
push WS_EX_OVERLAPPEDWINDOW
call CreateWindowEx
mov wndhand,eax ; Store the window handle.
push SW_SHOWNORMAL
push eax
call ShowWindow
push wndhand
call UpdateWindow
align 4
msgloop:
push 0
push 0
push NULL
push offset msgstru ; Address of the MSG structure.
call GetMessage
cmp eax,0
je msgloend
push offset msgstru
call TranslateMessage ; For some keyboard processing.
push offset msgstru
call DispatchMessage ; Sends a message to a wndproc.
jmp msgloop
align 4
msgloend:
push dword ptr msgstru[8]
call ExitProcess ; Terminates the program.
align 4
wndproc:
push ebp
mov ebp,esp
cmp dword ptr[ebp+12],WM_CREATE
jne paint
xor eax,eax
mov esp,ebp
pop ebp
ret 16
align 4
paint:
cmp dword ptr[ebp+12],WM_PAINT
jne destwnd
push offset painstru
push wndhand
call BeginPaint
mov hdc,eax
push offset rect
push wndhand
call GetClientRect ; Get the dimensions of the client area.
push DT_SINGLELINE or DT_CENTER or DT_VCENTER
push offset rect
push -1
push offset msghello
push hdc
call DrawText ; Write a string in the client area.
push offset painstru
push wndhand
call EndPaint
xor eax,eax
mov esp,ebp
pop ebp
ret 16
align 4
destwnd:
cmp dword ptr[ebp+12],WM_DESTROY
jne msgdefau
push 0
call PostQuitMessage
xor eax,eax
mov esp,ebp
pop ebp
ret 16
align 4
msgdefau:
push dword ptr[ebp+20]
push dword ptr[ebp+16]
push dword ptr[ebp+12]
push dword ptr[ebp+8]
call DefWindowProc ; Default message processing.
mov esp,ebp
pop ebp
ret 16
end start
It's guided in the book's example, is not a copy :icon_exclaim:. The comments aren't too good because i'm still learning the details of the functions. :P
¡Hola, Felipe!
If you will use "push WS_OVERLAPPEDWINDOW or WS_VISIBLE" then you do not need
push SW_SHOWNORMAL
push eax
call ShowWindow
push wndhand
call UpdateWindow
try to do so push NULL
push insthan
push NULL
push NULL
mov eax,CW_USEDEFAULT
push eax
push eax
push eax
push eax
push WS_OVERLAPPEDWINDOW or WS_VISIBLE
push offset wndname ; Address of the string displayed in the title bar.
push offset wndclaname ; Address of the WNDCLASSEX structure.
push WS_EX_OVERLAPPEDWINDOW
call CreateWindowEx
mov wndhand,eax ; Store the window handle.
mov edi,offset msgstru
msgloop:
push 0
push 0
push NULL
push edi ; Address of the MSG structure.
call GetMessage
or eax,eax
je msgloend
push edi
call TranslateMessage ; For some keyboard processing.
push edi
call DispatchMessage ; Sends a message to a wndproc.
jmp msgloop
P.S. Hi, jj2007! Grazie per l'emendamento!
Thanks Mikl_! I did it and all worked better. I liked those improvements. I certainly want to reach a good optimized assembly programming level. 8)
:icon14: :icon14: :icon14:
Bueno, Philipe, sabía que te gustaría.
invoke GetModuleHandle,NULL
mov x1,eax
invoke LoadIcon,NULL,IDI_APPLICATION
mov x2,eax
invoke LoadCursor,NULL,IDC_ARROW
mov x3,eax
push x1
push x2
push x3
push offset fmt
push offset buffer
call wsprintf
add esp,20
invoke MessageBox,NULL,addr buffer,addr MsgCaption,MB_OK
....
.data
fmt db "hCursor = %04Xh",0Ah,"hIcon = %04Xh",0Ah,"hInstance =%04Xh",0
buffer db 30 dup(?)
x1 dd ?
x2 dd ?
x3 dd ?
Una vez que escriba tal programa, observe qué valores devuelven las funciones GetModuleHandle, LoadIcon y LoadCursor, y no tiene que llamarlas una y otra vez cuando cree un programa de ventana. En tu versión de Windows, estos valores no cambian. Intenta hacer esto ...
The values are:
hCursor:10003h
hIcon:10027h
hInstance:400000h
I don't undestand. Maybe this values are always the same values and i don't have to call the GetModuleHandle function, but if i don't call the other two functions how i will load the icon and the cursor? Or you mean i just have to call them once in each program independently of the number of windows that i create? :redface:
¡Hola, Felipe!
example for your program; ¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤
; This program was started by Felipe in 2017-11-03.
; ¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\gdi32.lib
.data
wndname byte "WINDOW EXAMPLE",0 ; Title bar of the window.
msghello byte "Well, here it is a way of how to do a window in Windows. ",\
"There are many others by the way.",0 ; Message drawed in the window client area.
.data?
wndclaex WNDCLASSEX <>
msgstru MSG <>
hdc dword ?
painstru PAINTSTRUCT <>
rect RECT <>
wndhand dword ? ; Window handle
.code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Fill the WNDCLASSEX structure:
start:
mov wndclaex.cbSize,sizeof wndclaex ; Size of structure.
mov wndclaex.style,CS_HREDRAW or CS_VREDRAW; Style of class.
mov wndclaex.lpfnWndProc,offset wndproc ; Address of wndproc (callback proc).
mov wndclaex.cbClsExtra,NULL
mov wndclaex.cbWndExtra,NULL
mov wndclaex.hInstance,400000h ; Instance handle
mov wndclaex.hIcon,10003h ; icon
mov wndclaex.hCursor,10027h ; cursor
push WHITE_BRUSH
call GetStockObject
mov wndclaex.hbrBackground,eax
mov wndclaex.lpszMenuName,NULL
mov wndclaex.lpszClassName,offset wndname ; Address of the class name.
mov wndclaex.hIconSm,10003h
push offset wndclaex ; Address of the WNDCLASSEX structure.
call RegisterClassEx
push NULL
push 400000h ; Instance handle
push NULL
push NULL
mov eax,CW_USEDEFAULT
push eax
push eax
push eax
push eax
push WS_OVERLAPPEDWINDOW or WS_VISION
push offset wndname ; Address of the string displayed in the title bar.
push offset wndname ; Address of the WNDCLASSEX structure.
push WS_EX_OVERLAPPEDWINDOW
call CreateWindowEx
mov wndhand,eax ; Store the window handle
mov edi,offset msgstru ; Address of the MSG structure
msgloop:
push 0
push 0
push NULL
push edi ; Address of the MSG structure.
call GetMessage
push edi
call TranslateMessage ; For some keyboard processing
push edi
call DispatchMessage ; Sends a message to a wndproc.
jmp msgloop
wndproc:
push ebp
mov ebp,esp
cmp dword ptr [ebp+12],WM_CREATE
je create
cmp dword ptr[ebp+12],WM_PAINT
je paint
cmp dword ptr[ebp+12],WM_DESTROY
je destwnd
msgdefau:
leave
jmp DefWindowProc ; Default message processing
destwnd:
push 0
call ExitProcess ; Terminates the program
paint:
push offset painstru
push wndhand
call BeginPaint
mov hdc,eax
push offset rect
push wndhand
call GetClientRect ; Get the dimensions of the client area.
push DT_SINGLELINE or DT_CENTER or DT_VCENTER
push offset rect
push -1
push offset msghello
push hdc
call DrawText ; Write a string in the client area.
push offset painstru
push wndhand
call EndPaint
create:
xor eax,eax
leave
ret 16
end start
For your version of Windows, the values for the cursor and the icon will always be the same, so calling the functions of the LoadIcon and LoadCoursor is superfluous. To change the value that the function GetModuleHandle returns, when building the program, if you use the option /BASE:0xNumber If this option is not used, the default value of Number is 400000h always.
I see. I guess loadicon and loadcursor is just to get those constants. But what if i want a custom icon (like that in the program above, version 1)?
PD: cool stuff, if i store the icon in the cursor location in wndclass and viceversa i get in the client area of the window the icon for the program as the cursor. :lol:
Also i suppose that to run the program in another version of windows i will have to use those functions, right?
You will use those functions for to run the program in another version of windows, it is right. The version of Windows is like fashion. Most users (90%) have the same version of Windows. About icons and cursors
Quoteif i store the icon in the cursor location in wndclass and viceversa i get in the client area of the window the icon for the program as the cursor
you look in
Chapter #11. Brer Rabbit studies icons and cursors (https://wasm.in/threads/skazki-djadjushki-rimusa.31832/page-2#post-383779)
There are some things that I always make GLOBAL, the instance handle, main window handle, icon and cursor handles and any of those things that only need to be loaded once. If you are calling either external files like a DLL or a library module, you pass any of them as required. Things like this are determined at the scope level, things that you need as LOCAL within a procedure are easily dealt with as locals where data that is required across many different parts of an application are best dealt with as GLOBAL scope variables.
It was fashion in the early to middle 1990s to desperately avoid using GLOBAL variables as a prelude to OOP coding but it also produced some terrible code with multiple duplications of simple things like getting the instance handle "invoke GetModuleHandle,0" when you only ever needed to do this once and store it at GLOBAL scope. Also be careful about using the constant as the instance handle as this can be altered by the linker, one API call will not blow out the file size and it is the safe way to do it.
Good morning, hutch--!
you are right, but usually assembler programs are written for author-self, and not for commercial distribution, therefore I gave these advice to Felipe
Indeed i think is a very nice information about the windows system (great page).
But yes, for doing programs for others it's better to use the api calls that do the jobs.
:icon14: :icon14:
Quote from: felipe on November 05, 2017, 01:17:10 PMfor doing programs for others it's better to use the api calls that do the jobs.
Just imagine that in a year or two, your computer gets stolen and your new machine has Windows 11 installed. Do you want to rewrite all your code?
Buona mattina, jj2007!
I'm not Michelangelo di Lodovico di Leonardo di Buonarroti Simoni and my programs will become obsolete in a year and I will not write any more as I would write a year ago. And there will be commands that will no longer support new processors that were two or five years ago. Programming in assembler is fast ... and I do not need to rewrite ALL the code I wrote a year ago
I tend to walk a hard line here, there will always be hobbyist code that people use locally to get a job done and I have a reasonable amount of "Write once, Run once" code that I use a basic compiler for which I can pelt out in a few minutes to do things like write tables and similar messy things to write manually but there are only 2 classes of code that gets released, professional class high reliability binaries and TRASH.
The vast number of people who have written MASM code over the years need it for something serious and it must be professional quality and this means using properly documented functions (API and some VC runtime code), code that fully complies with the ABI for the platform (Intel for win32 and Microsoft for Win 64) and algorithm code that has had the guts kicked out of it to ensure that it is reliable across multiple versions of Windows. With 64 bit you can safely use most of the later instructions but you need to do a CPUID test if you want to use the latest SSE4.2 and AVX and later instructions.
Stay away from later OS versions of specific API code if you can do it with an earlier API as it will run on more processors and OS versions and write tricks and undocumented functions at your own risk as there is no reason to think they will be supported in later OS versions. The other thing that cannot be overstressed is understand and stick to the published ABI register usages as this has been reliable since the start of Win32. Same with Win 64, you have a Microsoft ABI on which registers to use and which to protect and if you get it wrong, it will explode in your face and make you look like a jerk who doesn't understand the OS specifications.
Quote from: Mikl__ on November 05, 2017, 03:21:00 PMmy programs will become obsolete in a year and I will not write any more as I would write a year ago. And there will be commands that will no longer support new processors that were two or five years ago
Carissimo Mikl,
A quick file search for the *.asc ending finds over 5,000 files in my \Masm32 folder, the oldest being about 10 years old. If
one of them doesn't build by hitting F6, alarm bells ring in my head, and I start investigating what may have changed. Same for GfaBasic files with the *.gfw extension, they build fine even if they date back 40 years.
You may call that paranoid, but it distinguishes a serious coder from a trial-and-error script kiddie. You are not in the latter category, you have proven that you can produce great stuff here, especially with the Iczelion tutorials that you ported to 64-bit land. Google
mikl Iczelion to see what I mean. People value your work.
The ability to build code that is more than a year old is also something that distinguishes the Masm32 SDK from "professional" packages like Visual Studio. The main reason why I don't take Micros**t seriously is indeed that everytime I find a little hello world proggie on the web that is older than, say: 2 years, and I try to load it into Visual Crap, the M$ flagship complains whiningly that it is unable to open such old stuff. Or it simply hangs. That is truely "professional", but guess what, we can do better, and we are proud of that :icon_mrgreen:
In short: Read carefully what Hutch writes above. He is 100% right ;-)
P.S.: "there will be commands that will no longer support new processors" - very unlikely. M$ is scared of breaking legacy code, and Intel/AMD sell CPUs for the M$ world. Even dinosaurs like
lodsb and
jecxz work just fine in 64-bit code (but aaa aad aam don't, see x64 Instructions (https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/x64-instructions)). Of course, one has to draw a line somewhere - nobody here wants to support Windows 95. My rule with MasmBasic is to use SSE2 and below in library routines, which means it is fast enough and will run on 99.x% of all machines. Similar for API calls: XP must support it; if a user decides that a Vista API call is needed, so be it - he is free to do that in "pure" assembly, but a library should give a high weight to compatibility (one reason btw why Linux never got a foot in the door).
Quote from: jj2007 on November 05, 2017, 08:42:57 PM
nobody here wants to support Windows 95.
:lol:
=======================================================================
I'm agreed with all of you, basically because you all know the difference between building windows's "standard applications" and something particular. The later can be of different kinds. Of course the later type also should be based in reasonable knowledge and experience. By just trying things and see what happen later it will be probably a loose of time (at least for someone). But i do support to professionals (and with that i just mean someone who understand what he/she is doing) with some special works. They may not run in more than a few machines, but that's precisely one of the main advantages of assembly programming: the specific thing. Of course most will say is a disadvantage.
You all also know how OS specific it is, so yeah if someone want's to try more special things and is an assembly programmer :P, if he/she knows what he/she is doing, so ok. If you don't know, don't loose your time in that way. First learn how things really work, then explore but based in what you already know of course.
Well at least that's how i work...
:icon14: :icon14:
I had been very bussy, but now i have more time to work on my codings. Here it is a premature version of what should be a more complete version of the next example program just based in the book. Remember, just based, is not a copy, it's just a guide.
The .exe is attached.
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; This program was started by Felipe at 2017-12-12.
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\gdi32.lib
.data
align 1
claname byte "TEXT CLASS",0
wndname byte "A WEIRD TEXT-GRAPHIC (WHAT?) PROGRAM JUST FOR ALL OF YOU!",0
textmsg byte "Hello friends, this is a very simple program. You can see it, hate it or laught of it."
byte "It's your choice... "
byte "I'm working with that old (may i say old?) book from Petzold, but i had to write this "
byte "before i finish the example that i'm studying. "
byte "In my opinion, or IMHO, GDI looks pretty cool stuff. "
byte "I hope you can visualize this text closely to normal, since i'm not trying to adjust "
byte "in the program nothing related to your system metrics. Sorry for that, you know i'm a "
byte "beginner. "
byte "Hey, i had an idea!: From now on all my post and replies will be in a window program "
byte "like this one, with my opinions hardcoded in a text like this... "
byte "You will have to download them and ...HAHAHAHA!!! You will hate me more, right? "
byte "Ok, no more jokes. "
.data?
align 4
hmodule dword ?
wndclaex dword 12 dup(?) ; The WNDCLASSEX structure.
hwnd dword ?
msgstru dword 7 dup(?) ; The MSG structure.
hdc dword ?
paintstru dword 12 dup(?) ; The PAINTSTRUCT structure.
.code
align 4
start:
push NULL
call GetModuleHandle
mov hmodule,eax
mov dword ptr wndclaex[0],sizeof wndclaex
mov dword ptr wndclaex[4],CS_HREDRAW or CS_VREDRAW
mov dword ptr wndclaex[8],wndproc
mov dword ptr wndclaex[12],0
mov dword ptr wndclaex[16],0
mov wndclaex[20],eax
push 500
push eax
call LoadIcon
mov wndclaex[24],eax
push IDC_ARROW
push NULL
call LoadCursor
mov wndclaex[28],eax
push WHITE_BRUSH
call GetStockObject
mov wndclaex[32],eax
mov dword ptr wndclaex[36],NULL
mov dword ptr wndclaex[40],offset claname
mov dword ptr wndclaex[44],NULL
push offset wndclaex
call RegisterClassEx
push NULL
push hmodule
push NULL
push NULL
push CW_USEDEFAULT
push CW_USEDEFAULT
push CW_USEDEFAULT
push CW_USEDEFAULT
push WS_OVERLAPPEDWINDOW
push offset wndname
push offset claname
push WS_EX_OVERLAPPEDWINDOW
call CreateWindowEx
mov hwnd,eax
mov esi,eax
push SW_SHOWNORMAL
push esi
call ShowWindow
push esi
call UpdateWindow
mov esi,offset msgstru
align 4
msgloop:
push 0
push 0
push NULL
push esi
call GetMessage
cmp eax,0
jz endmsglop
push esi
call TranslateMessage
push esi
call DispatchMessage
jmp msgloop
align 4
endmsglop:
push dword ptr[esi+8]
call ExitProcess
align 4
wndproc:
push ebp
mov ebp,esp
cmp dword ptr[ebp+12],WM_PAINT
jne destroy
push offset paintstru
push hwnd
call BeginPaint
mov hdc,eax
push 00e00000h ; Blue color for the text.
push eax
call SetTextColor
mov esi,offset textmsg
xor edi,edi ; The y coordinate.
mov ebx,12 ; Number of lines.
align 4
allstri:
push 86 ; Number of characters per line.
push esi ; Line addresses.
push edi ; Y coordinate.
push 0 ; X coordinate.
push hdc
call TextOut
add edi,25 ; Increase y coordinate.
add esi,86 ; Get the address of the next line of text.
dec ebx ; Decrease the total of lines to print (draw).
jnz allstri ; Repeat until all the string are drawed.
push offset paintstru
push hwnd
call EndPaint
xor eax,eax
mov esp,ebp
pop ebp
ret 16
align 4
destroy:
cmp dword ptr[ebp+12],WM_DESTROY
jne wnddefau
push 0
call PostQuitMessage
xor eax,eax
mov esp,ebp
pop ebp
ret 16
align 4
wnddefau:
push dword ptr[ebp+20]
push dword ptr[ebp+16]
push dword ptr[ebp+12]
push dword ptr[ebp+8]
call DefWindowProc
mov esp,ebp
pop ebp
ret 16
end start
You could refine it and make it more efficient if you create the WNDCLASSEX structure directly on the stack: push NULL ; last arg of CreateWindowEx
push NULL
call GetModuleHandle
push eax ; 2nd last arg of CreateWindowEx
push 32512
push eax
xchg eax, ebx
call LoadIcon
push NULL ; wndclaex[44]
push offset claname
push NULL ; wndclaex[36]
push WHITE_BRUSH
call GetStockObject
push eax ; wndclaex[32]
push IDC_ARROW
push NULL
call LoadCursor
push eax ; wndclaex[28]
push 32512
push ebx
call LoadIcon
push eax ; wndclaex[24]
push ebx ; wndclaex[20]
push 0 ; wndclaex[16]
push 0 ; wndclaex[12]
push wndproc
push CS_HREDRAW or CS_VREDRAW
push WNDCLASSEX
push esp
call RegisterClassEx
push NULL
push NULL
push CW_USEDEFAULT
push CW_USEDEFAULT
push CW_USEDEFAULT
push CW_USEDEFAULT
push WS_OVERLAPPEDWINDOW
push offset wndname
push offset claname
push WS_EX_OVERLAPPEDWINDOW
call CreateWindowEx
add esp, WNDCLASSEX
Man, i like it and i understood the sequence, but i'm a little confused with this: :redface:
push WNDCLASSEX ; How this could give us
push esp ; the size of the structure?
And this:
add esp, WNDCLASSEX ; Wow is possible to do this! (I already have assemble the code without errors).
I will check the code in a moment with a debugger to understand this three statments (instructions) later. I guess you were assuming a local wndclassex, but it make no sense for me by now, because looks like you don't use that local space. I mean you are using part of it but not all of it (maybe all the program is local?).
I will try it with a debugger in a moment. Thanks btw, i think it's a great job! :icon14:
(But i need to know it for sure) :biggrin:
Ok jj i solved the puzzle: :biggrin:
push WNDCLASSEX ; The size of the structure (in bytes).
push esp ; The starting address of the structure (wich is in the stack).
then:
add esp, WNDCLASSEX ; Discarding the structure from the stack.
But this later statement most be located inmediately after registering the class and before continuing pushing the remainders parameters for the CreateWindowsEx function.
You are a hard teacher... :redface:
:P
Btw, one more thing: I'm still confused how WNDCLASSEX can be interpreted as a number of bytes. This only happen when we push the structure in the stack?, Why use sizeof if not? You have to admit that this is weird and if not, please explain me that. :greensml:
Thanks.
:icon14:
Quote from: felipe on December 14, 2017, 05:16:40 AMI'm still confused how WNDCLASSEX can be interpreted as a number of bytes.
WNDCLASSEX, RECT, DWORD etc are structures or types, but they are also just immediate numbers. Test it:
include \masm32\include\masm32rt.inc
.code
sometest proc
ret
sometest endp
start:
print str$(BYTE), 9, "a byte", 13, 10
print str$(REAL8), 9, "a Real8", 13, 10
print hex$(sometest)," a proc", 13, 10
print hex$(WNDCLASSEX), " a struct", 13, 10
print str$(sometest), 9, "a proc", 13, 10
inkey str$(WNDCLASSEX), 9, "a struct", 13, 10
exit
end start
1 a byte
8 a Real8
00401000 a proc
00000030 a struct
4198400 a proc
48 a struct
Cool stuff! Immediate numbers indicating the size in bytes of each one. So they should be just labels for the assembler for indicating to it how many bytes to store, reserve, etc. Nice to know this.
Quote from: felipe on December 14, 2017, 05:16:40 AM
You are a hard teacher... :redface:
:P
As a matter of fact you are a very good one. ;) Thanks.
:t