News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Win32 My First Window

Started by tda0626, April 24, 2024, 11:05:19 AM

Previous topic - Next topic

sinsi

Quote from: tda0626 on April 28, 2024, 04:18:02 AMCan you give me a rundown on the two problems that were in it?
The first one was not passing the parameters to WinMain, which sudoku pointed out
start:

push 0
call GetModuleHandle

mov hinst, eax
call GetCommandLine

mov CommandLine, eax

;missing 4 push <<<<<<<<
call WinMain

push eax

Call ExitProcess

WinMain Proc hinstance:HINSTANCE, hprevinstance:HINSTANCE, lpCmdLine:LPSTR, nCmdShow:dword
This means that when you call CreateWindowEx, you push hinstance which isn't initialised to anything.

The second one was your WndProc
.if umsg==WM_PAINT
                ;falls through to .endif
.elseif umsg==WM_DESTROY
                ;falls through to .endif
.else
push lParam
push wParam
push umsg
push hWnd
call DefWindowProc
                ;EAX has a potentially useful value now, but it gets wiped out
.endif
xor eax, eax
ret
Windows messages rely on a return value in EAX, 0 usually indicates that a message was handled but some messages expect e.g. a handle to a brush, so returning 0 tells the sender to stop what it was doing.


Quote from: tda0626 on April 28, 2024, 04:18:02 AMIs wParam a return value from a message?
wParam is the argument you pass to PostQuitMessage, a parent program can query it with GetExitCodeProcess


Just to keep the echo going, JZ/JNZ tests the Zero flag, usually used for add/sub when the result could be 0.
JE/JNE is the same, but would be used when comparing where the result is equal or not equal.
English syntax  :rolleyes:
😁

NoCforMe

Quote from: sinsi on April 28, 2024, 05:18:14 AMJust to keep the echo going, JZ/JNZ tests the Zero flag, usually used for add/sub when the result could be 0.
JE/JNE is the same, but would be used when comparing where the result is equal or not equal.
English syntax  :rolleyes:

Yes, they (JZ/JE) are exactly the same functionally (they both test the zero flag). I prefer to use JE in cases where I'm doing a numeric comparison, and JZ when I'm explicitly testing for zero:
CMP     someVar, 42      ;Do a numeric comparison
JE      is42

TEST    someVar, 1       ;Test a flag bit
JZ      notSet

TEST    EAX, EAX         ;Check if a register is 0
JZ      is0
but this is only a stylistic preference on my part.
Assembly language programming should be fun. That's why I do it.

tda0626

Quote from: NoCforMe on April 28, 2024, 05:17:05 AMSome general advice to the OP here (and remember that this is free advice which you are free to take or ignore):

Since you're an asm beginner, don't copy code that you don't understand and try to make it work. If you're looking at someone's code (C, asm, doesn't matter), make sure you understand what it does and how it works before trying to implement it yourself. Otherwise you'll just be trying this and trying that, maybe getting it to work but still not understanding why it works.

This is important. If you look at some code and can't understand what it does, pick it apart piece by piece. Look up the functions, messages and structures on Microsoft's documentation site, Microsoft Learn, and pay attention to the descriptions there.

Or ask here. As you can see, plenty of folks ready to help you.

End of advice.


Appreciate the advice. With this window program, I understood the steps mostly but there were gaps in my knowledge that everyone here helped out on and for that I am most grateful. For a next step in the Windows world, what would you recommend?

@Sudoku

Your Google Fu is better than mine. I looked and looked but didn't even come close to finding anything on wParam other than the basic definition of it which is vague. That 1st link explained it very well and didn't remember that PostQuitMessage returned it.


@sinsi

Thanks for the breakdown!

NoCforMe

Quote from: tda0626 on April 28, 2024, 12:17:56 PMI looked and looked but didn't even come close to finding anything on wParam other than the basic definition of it which is vague.

The definition of wParam has got to be vague by its nature. First of all, it's the third parameter to any window procedure:



But that tells us nothing, because it means different things under different circumstances. (They might as well call it "additional information parameter #1".) The meaning depends on what message is being sent to the window (remember Windows is a message-based OS). You have to look up the particular message to see what wParam is being used for. For example, let's say you want to change the font of a certain control (a control being a window with a window procedure). You'd use the WM_SETFONT message, which uses wParam and lParam thus:

QuoteParameters:
wParam
A handle to the font (HFONT). If this parameter is NULL, the control uses the default system font to draw text.
lParam
The low-order word of lParam specifies whether the control should be redrawn immediately upon setting the font. If this parameter is TRUE, the control redraws itself.

(from Windows Learn--learn how to dig into this repository)

So when you send this message to the control window, you set wParam to the handle to the font and lParam to either TRUE or FALSE depending.

This applies as well to messages that get sent to you in your window procedure. For instance, if you handle the WM_CTLCOLORSTATIC message (used to change the display color of static controls), wParam is set to the hDC of the control so that you can change its foreground and background colors. (Here @ Microsoft Learn)

Other messages have completely different uses for these two parameters. Some messages don't even use them at all (or use just one or the other).

(Meta question: how do I get my images to show larger so you don't have to click on them to see them? I used the "do not resize my image" but it resized it anyhow!)
Assembly language programming should be fun. That's why I do it.

jj2007

A word on the WndProc, the place where Windows sends messages, and you can react to them:

WndProc proc uses esi edi hWnd, uMsg, wParam:WPARAM, lParam:LPARAM

  SWITCH uMsg

  CASE WM_CREATE ; this message serves to initialise your application
invoke CreateWindowEx, WS_EX_CLIENTEDGE, chr$("edit"), ...

  CASE WM_COMMAND ; react to menu clicks

  CASE WM_CLOSE ; decide whether to close or not
MsgBox 0, "Sure to close?", "Hi", MB_YESNO
sub eax, IDNO ; don't close if there is unsaved content
je @RetEax ; return eax, in this case zero (i.e. refuse to close)

  CASE WM_DESTROY ; quit after WM_CLOSE
invoke PostQuitMessage, NULL
  ENDSW

  invoke DefWindowProc, hWnd, uMsg, wParam, lParam ; let Windows do the default processing

@RetEax: ; this label serves to bypass DefWindowProc
  ret ; ret is a macro that pops the "uses" regs, then inserts retn 4*#args
WndProc endp

- SWITCH/CASE/ENDSW are Masm32 SDK macros. Some prefer chains of .if uMsg==WM ... .elseif ... .endif chains, but under the hood they are identical, and IMHO switches are far more readable

- There is an old debate, in the asm and C/C++ worlds, whether DefWindowProc should come under the last .else clause (when using chains), or whether it should be placed at the very end. IMHO it should be placed at the end, because there are 3 use cases:

1. You just grab information (size, x pos, ...) from a message; Windows must continue with the default processing.

2. You return a flag to Windows, e.g. zero: xor eax, eax, then ret. No further processing please.

3a. You want to change something, like painting an emoji, but then Windows must continue with the default processing to finalise the task.

3b. Rare case: you want to change something, but first Windows must do its job: call DefWindowProc, then do your emoji painting etc, then decide whether you are done (ret), or whether Windows must continue with the default processing to finalise the task.

All three scenarios work fine with a single invoke DefWindowProc at the very end.

I've added the @RetEax trick; you could also use e.g. mov eax, 1, then ret, but when looking at such code with a debugger (try Olly), you will find out that the simple ret is actually quite a chain of instructions; so it is shorter to jump to one central @RetEax: label.

NoCforMe

Quote from: jj2007 on April 28, 2024, 06:40:48 PM- SWITCH/CASE/ENDSW are Masm32 SDK macros. Some prefer chains of .if uMsg==WM ... .elseif ... .endif chains

And some of us (me, f'rinstance) prefer not to use any of those macros that make your code look like C, and instead do things the good old fashioned way:
TheDialogProc PROC hWin:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD

MOV EAX, uMsg
CMP EAX, WM_INITDIALOG
JE do_init
CMP EAX, WM_COMMAND
JE do_command
CMP EAX, WM_CLOSE
JE do_close

dodefault:
XOR EAX, EAX
RET

(Since this is a dialog window proc, there's no need for DefWindowProc().)
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: NoCforMe on April 29, 2024, 11:23:07 AMsome of us (me, f'rinstance) prefer not to use any of those macros that make your code look like C

It's not just C - most languages (Basic, Java, Rust, Pascal, ...) have a Switch (or Select) ... Case construct. It's for readability.

Btw is \Masm32\examples\exampl07\slickhuh\slickhuh.asm still part of the Masm32 SDK?

daydreamer

Quote from: jj2007 on April 29, 2024, 05:10:14 PM
Quote from: NoCforMe on April 29, 2024, 11:23:07 AMsome of us (me, f'rinstance) prefer not to use any of those macros that make your code look like C

It's not just C - most languages (Basic, Java, Rust, Pascal, ...) have a Switch (or Select) ... Case construct. It's for readability.

Btw is \Masm32\examples\exampl07\slickhuh\slickhuh.asm still part of the Masm32 SDK?
I prefer masm  style switch / case over c/ CPP style where you need extra type break; after each case
And assembler with all its macros make code productivity good vs bare metal style
I prefer to concentrate on make big program finished with help of macros


my none asm creations
https://masm32.com/board/index.php?topic=6937.msg74303#msg74303
I am an Invoker
"An Invoker is a mage who specializes in the manipulation of raw and elemental energies."
Like SIMD coding

NoCforMe

Quote from: jj2007 on April 29, 2024, 05:10:14 PMBtw is \Masm32\examples\exampl07\slickhuh\slickhuh.asm still part of the Masm32 SDK?
Yikes!!!
It's in my installation. Copying it here in all its glory: just in case it isn't clear, this is a great example of how not to code assembly language. (All it does is create an empty window.) Check it out:

    .686p
    .model flat, stdcall
    option casemap :none
    externdef _imp__DispatchMessageA@4:PTR pr1
    m2wp equ <_imp__DispatchMessageA@4>
    externdef _imp__GetMessageA@16:PTR pr4
    gms equ <_imp__GetMessageA@16>
    externdef _imp__DefWindowProcA@16:PTR pr4
    dpro equ <_imp__DefWindowProcA@16>
    externdef _imp__PostQuitMessage@4:PTR pr1
    pqm equ <_imp__PostQuitMessage@4>
    externdef _imp__RegisterClassExA@4:PTR pr1
    scln equ <_imp__RegisterClassExA@4>
    externdef _imp__ShowWindow@8:PTR pr2
    wshw equ <_imp__ShowWindow@8>
    externdef _imp__LoadCursorA@8:PTR pr2
    lsc equ <_imp__LoadCursorA@8>
    externdef _imp__CreateWindowExA@48:PTR pr12
    crwe equ <_imp__CreateWindowExA@48>
    includelib \masm32\lib\user32.lib
  .code
    ims db "Slick Huh ?", 0
    pcl dd ims
  slick_huh:
    push ebp
    mov ebp, esp
    sub esp, 96
    push 32512
    xor edi, edi
    push edi
    mov esi, 4194304
    mov ebx, pcl
    call lsc
    mov DWORD PTR [ebp-96], 48
    mov DWORD PTR [ebp-92], 3
    mov DWORD PTR [ebp-88], OFFSET wpep
    mov DWORD PTR [ebp-84], edi
    mov DWORD PTR [ebp-80], edi
    mov DWORD PTR [ebp-76], esi
    mov DWORD PTR [ebp-72], edi
    mov DWORD PTR [ebp-68], eax
    mov DWORD PTR [ebp-64], 10h
    mov DWORD PTR [ebp-60], edi
    mov DWORD PTR [ebp-56], ebx
    mov DWORD PTR [ebp-52], edi
    lea eax, [ebp-96]
    push eax
    call scln
    mov ecx, -2147483648
    push 1
    push edi
    push esi
    push edi
    push edi
    push edi
    push ecx
    push edi
    push ecx
    push 13565952
    push ebx
    push ebx
    push edi
    call crwe
    push eax
    call wshw
    lea ebx, [ebp-48]
    push edi
    push edi
    push edi
    push ebx
    jmp mlep
  @@: push edi
    push edi
    push edi
    push ebx
    push ebx
    call m2wp
    mlep: call gms
    test al, al
    jnz @B
    leave
    retn
  wpep: cmp DWORD PTR [esp+8], 2
    jne @F
    push 0
    call pqm
  @@: jmp dpro
  end slick_huh
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: NoCforMe on April 30, 2024, 05:41:21 AMCopying it here in all its glory

Hey, no macros used - you must love it :badgrin:  :greensml:  :rofl:

tda0626

Hey everyone again!

So I made a new program using the invokes and switch macro and everything seems to work ok except for the code in the WM_PAINT section. It doesn't print the string to the center of the window client area. Anyone know why?


Source code is attached...

Tim

jj2007

      invoke BeginPaint, hWnd, addr ps
      mov hdc, eax
      invoke GetClientRect, hWnd, addr rect
      invoke DrawText, hdc, offset greeting, ...

tda0626

Thanks JJ. I feel so stupid  :joking:

NoCforMe

Quote from: tda0626 on May 01, 2024, 08:53:56 AMThanks JJ. I feel so stupid  :joking:
My new friend, join the club. I don't mean the Stupid Persons Club, but the club of all those who've made ... stupid mistakes. Like all of us at one time or another. (Of course, *I* never made any of those mistakes ...)

BTW, one small refinement to your WM_PAINT handler, hopefully teach you a little something: you have
invoke BeginPaint, hWnd, addr ps
mov    hdc, eax
invoke GetClientRect, hWnd, addr rect
invoke DrawText, hdc, offset greeting, -1, addr rect, DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint, hWnd, addr ps
ret
You don't need to call GetClientRect(), because guess what?  that PAINTSTRUCT (ps)that you got from BeginPaint() already contains a rectangle which you can then pass to DrawText():
invoke DrawText, hdc, offset greeting, -1, addr ps.rcPaint, ...
Assembly language programming should be fun. That's why I do it.

tda0626

Quote from: NoCforMe on May 01, 2024, 09:41:00 AM
Quote from: tda0626 on May 01, 2024, 08:53:56 AMThanks JJ. I feel so stupid  :joking:

You don't need to call GetClientRect(), because guess what?  that PAINTSTRUCT (ps)that you got from BeginPaint() already contains a rectangle which you can then pass to DrawText():
invoke DrawText, hdc, offset greeting, -1, addr ps.rcPaint, ...


Good to know! Thanks!