The MASM Forum

General => The Campus => Topic started by: tda0626 on April 24, 2024, 11:05:19 AM

Title: Win32 My First Window
Post by: tda0626 on April 24, 2024, 11:05:19 AM
Hello,

I programmed my first Win32 Window but it immediately closes once it runs. I was wondering if someone could look over my code and see what I have done wrong or even make some suggestions. I went by a template in C on Microsoft's website so not sure what I did wrong.

.386
.model flat, stdcall
option    casemap:none

include    E:\masm32\include\windows.inc
include    E:\masm32\include\user32.inc
include    E:\masm32\include\kernel32.inc
include    E:\masm32\include\gdi32.inc

includelib    E:\masm32\lib\user32.lib
includelib    E:\masm32\lib\kernel32.lib
includelib    E:\masm32\lib\gdi32.lib

WindowHeight    equ    640
WindowWidth        equ 480



.data


szWindowClass    db "Desktop App",0
szTitle            db "My Window Title",0
reg_failed        db "Call to CreateWindow failed!",0
mywindow        db "This is my window!",0
greeting        db "Hello There!"
wc    WNDCLASSEX    {0}        ;Allocate Space for Window Class Structure
ps    PAINTSTRUCT    {0}        ;Allocate Space for Paint Structure -- For WM_PAINT


.code

start:
   

WinMain Proc hinstance:HINSTANCE, hprevinstance:HINSTANCE, lpCmdLine:LPSTR, nCmdShow:dword
   
    ;;;;;;;;;;;;;;;;;;;;;;
    ;Extra Local Variables
    ;;;;;;;;;;;;;;;;;;;;;;
   
    local msg:MSG
    local hwnd:HWND
   
    ;;;;;;;;;;;;;;;;;;;;;;;
    ;Window Class Structure
    ;;;;;;;;;;;;;;;;;;;;;;;
   
    mov wc.cbSize, sizeof WNDCLASSEX
    mov wc.style, CS_HREDRAW or CS_VREDRAW
    mov wc.lpfnWndProc, offset WndProc
    mov  wc.cbClsExtra, 0
    mov  wc.cbWndExtra, 0
    push hinstance
    pop wc.hInstance
    mov wc.hIcon, 0
    mov wc.hCursor, 0
    mov wc.hbrBackground, COLOR_WINDOW+1
    mov wc.lpszMenuName, 0
    mov wc.lpszClassName, offset szWindowClass
    mov wc.hIconSm, 0
   
    ;;;;;;;;;;;;;;;;;;;;;;
    ;Register Window Class
    ;;;;;;;;;;;;;;;;;;;;;;
   
        lea eax, wc
        push eax
        call RegisterClassEx
        cmp eax, 0
        jnz RegisterSuccess
   
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;Check for Registration Error
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   
    RegisterFailed:
        push 0
        push offset szTitle
        push offset reg_failed
        push 0
        call MessageBox
        ret
   
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;If Registration is a success continue
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   
    RegisterSuccess:
   
    ;;;;;;;;;;;;;;;
    ;Create Window
    ;;;;;;;;;;;;;;
   
    push 0
    push hinstance
    push 0
    push 0
    push WindowWidth
    push WindowHeight
    push CW_USEDEFAULT
    push CW_USEDEFAULT
    push WS_OVERLAPPEDWINDOW + WS_VISIBLE
    lea    eax, mywindow
    push eax
    lea  eax, szWindowClass
    push eax
    push WS_EX_WINDOWEDGE or WS_EX_CLIENTEDGE
    call CreateWindowEx
   
    ;Return Value of CreateWindowEx is a handle to a window -- Move return value into hwnd
   
    mov hwnd, eax
   
    ;Check to see if Create Window Failed
   
    cmp eax, 0
    jz RegisterFailed
   
    ;Update the Window
   
    push hwnd
    call UpdateWindow
    push nCmdShow
    push hwnd
    call ShowWindow
   
    ;;;;;;;;;;;;;
    ;Message Loop
    ;;;;;;;;;;;;;   
   
    .while eax !=0
    push 0
    push 0
    push hwnd
    lea eax, msg
    push eax
    call GetMessage   
    lea eax, msg
    push eax
    call TranslateMessage
    lea eax, msg
    push eax
    call DispatchMessage
    .endw
   
   
    ret
   
WinMain endp


WndProc Proc hWnd:HWND, umsg:dword, wParam:WPARAM, lParam:LPARAM
   
    ; Define a local variable to a handle to device context
   
    local hdc:HDC
   
    ;;;;;;;;;;;;;;;;;;;;;;;;
    ;Process Window Messages
    ;;;;;;;;;;;;;;;;;;;;;;;;
   
    .if umsg==WM_PAINT
        push offset ps
        push hWnd
        call BeginPaint
        mov hdc, eax
        push lengthof greeting
        push offset greeting
        push 5
        push 5
        push hdc
        call TextOut            ;Print String Inside Window
        push offset ps
        push hWnd
        call EndPaint
        ret
    .elseif umsg==WM_DESTROY
        push 0
        call PostQuitMessage
        ret
    .else   
        push lParam
        push wParam
        push umsg
        push hWnd
        call DefWindowProc
        ret
    .endif
   
 WndProc endp
   



end start

Title: Re: Win32 My First Window
Post by: NoCforMe on April 24, 2024, 11:20:41 AM
Just a few things at first:

1. As long as you create your window with the WS_VISIBLE style, no need to call UpdateWindow() or ShowWindow(); the window will appear as soon as it's created.

2. Please, please don't use the old-school "push-push-call" style! It's not only ugly but error prone (especially when using a function that takes lots of parameters like CreateWindowEx(). Use INVOKE instead:

INVOKE CreateWindowEx,
Exstyles, ;EXstyles
className, ;class name
windowName, ;window title/text
styles, ;styles
x, ;X-pos
y, ;Y-pos
width, ;width
height, ;height
parent, ;parent
menu, ;menu/ID
instance, ;instance handle
param ;param

Sorry, can't tell why the window closes immediately, but I'll keep looking over your code.
Title: Re: Win32 My First Window
Post by: tda0626 on April 24, 2024, 11:29:35 AM
Quote1. As long as you create your window with the WS_VISIBLE style, no need to call UpdateWindow() or ShowWindow(); the window will appear as soon as it's created.

2. Please, please don't use the old-school "push-push-call" style! It's not only ugly but error prone (especially when using a function that takes lots of parameters like CreateWindowEx(). Use INVOKE instead:

Thanks for the tips!

Tim
Title: Re: Win32 My First Window
Post by: Greenhorn on April 24, 2024, 12:02:28 PM
.if umsg==WM_PAINT
    ...
    xor  eax, eax    ; return zero !
    ret
.elseif umsg==WM_DESTROY
    ...
    xor  eax, eax    ; return zero !
    ret

You can also put the ret instruction at the end of the WndProc only once, instead of putting it after each message.

.if umsg==WM_PAINT
    ...
    xor  eax, eax    ; return zero !
.elseif umsg==WM_DESTROY
    ...
    xor  eax, eax    ; return zero !
.else
push lParam
push wParam
push umsg
push hWnd
call DefWindowProc    ; return result of call
.endif
ret
WndProc endp
Title: Re: Win32 My First Window
Post by: sudoku on April 24, 2024, 12:05:06 PM
I don't see a call to ExitProcess anywhere in your code..
That's not making it 'not run' though, just mentioning it.

In your Masm32 directory, there is a folder "examples" that will help you get started.
You might also want to get a debugger. I recommend ollydbg version 1.10 https://www.ollydbg.de/odbg110.zip (https://www.ollydbg.de/odbg110.zip)

This will help you debug your code.
Title: Re: Win32 My First Window
Post by: sinsi on April 24, 2024, 12:07:31 PM
call ShowWindow

;;;;;;;;;;;;;
;Message Loop
;;;;;;;;;;;;;

.while eax !=0
Have a guess what ShowWindow returns...
Title: Re: Win32 My First Window
Post by: NoCforMe on April 24, 2024, 12:11:17 PM
Yes; to explain Greenhorn's reply, you need to realize that in Windows programming, it's (usually) important what you return when you handle a message (or don't handle it) in a window procedure. In most cases, returning zero means "I handed this message", which tells Windows that it can remove your WM_PAINT request from the message queue.

If you're not already familiar with it, you should be referring to Microsoft's extensive Windows documentation repository. It's huge, and can be confusing. Here's one way to access it: Windows functions in alphabetical order (https://learn.microsoft.com/en-us/previous-versions//aa383688(v=vs.85)?redirectedfrom=MSDN).

Here's the documentation on WM_PAINT (https://learn.microsoft.com/en-us/windows/win32/gdi/wm-paint).
Title: Re: Win32 My First Window
Post by: Greenhorn on April 24, 2024, 12:20:56 PM
Quote from: sinsi on April 24, 2024, 12:07:31 PM    call ShowWindow
   
    ;;;;;;;;;;;;;
    ;Message Loop
    ;;;;;;;;;;;;;   
   
    .while eax !=0
Have a guess what ShowWindow returns...

True.

And check for errors.

    .while (TRUE)
        invoke GetMessage, addr msg, 0, 0, 0
        .break .if (!eax)
        .if (eax == -1)
            ; Check for error
           
            invoke MessageBox,
                    NULL,
                    addr szErrorMsg,
                    addr szAppName,
                    MB_ICONERROR or MB_OK
            mov eax, -1
            ret
        .endif
        invoke TranslateAccelerator, g_hwndMain, haccel, addr msg
        .if (!eax)
            invoke TranslateMessage, addr msg
            invoke DispatchMessage,  addr msg
        .endif
    .endw

    mov eax, msg.wParam
    ret
WinMain endp

Title: Re: Win32 My First Window
Post by: sinsi on April 24, 2024, 12:24:17 PM
Your message loop logic is wrong, you need to check the return value of GetMessage.
Here's a snippet from a MASM32 example
    StartLoop:
      invoke GetMessage,ADDR msg,NULL,0,0         ; get each message
      cmp eax, 0                                  ; exit if GetMessage()
      je ExitLoop                                 ; returns zero
      invoke TranslateMessage, ADDR msg           ; translate it
      invoke DispatchMessage,  ADDR msg           ; send it to message proc
      jmp StartLoop
    ExitLoop:
Title: Re: Win32 My First Window
Post by: jj2007 on April 24, 2024, 04:27:10 PM
Quote from: sinsi on April 24, 2024, 12:07:31 PMHave a guess what ShowWindow returns...

Sinsi nailed it, as usual ;-)

Hi Tim,

Read carefully what the others wrote, they are all right. Re push-push-pop style: that's not the immediate reason why your code fails, but try (after fixing the bug found by Sinsi)

WindowHeight    equ    140 ; <<<
WindowWidth    equ    480

You will be surprised ;-)

The built-in invoke macro prevents you from committing such blunders, and even barks at you if you didn't count correctly to 12. Besides, it is easier to translate C to ASM because it looks almost like Microsoft's example code:
  invoke CreateWindowEx, WS_EX_ACCEPTFILES, addr szWindowClass, addr mywindow,
     WS_OVERLAPPEDWINDOW or WS_VISIBLE,
     CW_USEDEFAULT, CW_USEDEFAULT, WindowWidth, WindowHeight,
     0, 0, wc.hInstance, NULL

Regarding the message loop, have a look at this template (https://masm32.com/board/index.php?msg=117713) (93 lines, with a menu, an edit control and a WM_PAINT handler) or at a loop that is foolproof (https://masm32.com/board/index.php?msg=127945).

Two minor problems:
1. Or, not add:
push WS_OVERLAPPEDWINDOW + WS_VISIBLEWhen combining two flags, never use the plus sign. Use or instead. It's not a problem in this specific case, but Windows does have constants composed of more than one value, and adding instead of oring will lead to bugs that are difficult to chase.

2. include    E:\masm32\include\windows.inc
That will not work for most of us here who don't have their Masm32 SDK on drive E: :cool:
Use include \masm32\include\windows.inc, i.e. no drive letter, just a backslash.

Even better, use include \masm32\include\masm32rt.inc (open that file in Notepad, and you'll understand why it's better).

P.S., reply #7: The error handler inside the message loop is not a good idea.
Title: Re: Win32 My First Window
Post by: Vortex on April 25, 2024, 05:02:50 AM
Hi tda0626,

You would like to visit Iczelion's article :

QuoteTutorial 3: A Simple Window

http://www.interq.or.jp/chubu/r6/masm32/tute/tute003.html

The example on the same page :

http://www.interq.or.jp/chubu/r6/masm32/tute/zips/tut03.zip
Title: Re: Win32 My First Window
Post by: tda0626 on April 25, 2024, 08:59:14 AM
Hey everyone! Thank you all for your help!

OK, I got it to work after fixing the loop logic and applied some of the suggested code edits. It displays fine now and it maximizes, minimizes, resize, ect. However, after closing the window, it is still showing up in my process list and I have to force close it. I added ExitProcess to the end of WinMain but it appears it doesn't close the process. Any ideas why the program is still running?


Tim
Title: Re: Win32 My First Window
Post by: sudoku on April 25, 2024, 09:07:48 AM
attach a zip file of what you now have. :smiley:
Title: Re: Win32 My First Window
Post by: NoCforMe on April 25, 2024, 09:32:52 AM
Quote from: tda0626 on April 25, 2024, 08:59:14 AMHey everyone! Thank you all for your help!

Something that might help in your beginning assembly-language programming efforts:
Once you get the hang of it you're not going to want to create each new program from scratch.
This is my "skeleton" that I use to start a new Windows GUI program with a main window. Fill it in with your stuff and it's guaranteed to work. It creates an empty window, centered on the desktop.

Not trying to force you into my particular programming style. We each have our preferred style; you'll find yours. Feel free to slice, dice and hack this any way you like.

;============================================
; -- Skeleton --
; -- [project title here] --
;============================================

.nolist
include \masm32\include\masm32rt.inc
.list

;============================================
; Defines, prototypes, etc.
;============================================

$mainWinWidth EQU 600
$mainWinHeight EQU 500

;===== Window styles: =====
; Change 1st 2 styles to "WS_OVERLAPPEDWINDOW" if you want a resizeable window:
$mainWinStyles EQU WS_CAPTION or WS_SYSMENU or WS_CLIPCHILDREN or WS_VISIBLE

;===== Window background colors: =====
$bkRED EQU 254
$bkGRN EQU 243
$bkBLUE EQU 199

$BkColor EQU $bkRED OR ($bkGRN SHL 8) OR ($bkBLUE SHL 16)

$SBID EQU 1111
$SBX EQU 0
$SBY EQU $mainWinHeight - $SBHeight
$SBWidth EQU $mainWinWidth
$SBHeight EQU 40
$numStatusParts EQU 4

$SB_styles EQU WS_CHILD OR WS_VISIBLE

;============================================
; HERE BE DATA
;============================================
.data

WC WNDCLASSEX < SIZEOF WNDCLASSEX, \
CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW, \
NULL, \ ;lpfnWndProc
NULL, \ ;cbClsExtra
NULL, \ ;cbWndExtra
NULL, \ ;hInstance
NULL, \ ;hIcon
NULL, \ ;hCursor
NULL, \ ;hbrBackground
NULL, \ ;lpszMenuName
NULL, \ ;lpszClassName
NULL > ;hIconSm


StatusParts DD 50, 150, 250, -1

NullString DB 0

MainClassName DB "[class name here]", 0

MainTitleText DB "[window title here]", 0

;============================================
; UNINITIALIZED DATA
;============================================
.data?

MainWinHandle HWND ?

InstanceHandle HINSTANCE ?

StatusHandle HWND ?
SBheight DD ?

;============================================
; CODE LIVES HERE
;============================================
.code

start: INVOKE GetModuleHandle, NULL
MOV InstanceHandle, EAX

CALL WinMain
INVOKE ExitProcess, EAX

;====================================================================
; Mainline proc
;====================================================================

WinMain PROC
LOCAL msg:MSG, brush:HBRUSH, wX:DWORD, wY:DWORD, gpRect:RECT

; Create  brush to set background color:
INVOKE CreateSolidBrush, $BkColor
MOV brush, EAX

; Register class for parent window:
MOV EAX, InstanceHandle
MOV WC.hInstance, EAX
MOV WC.lpfnWndProc, OFFSET MainWindowProc
MOV EAX, brush
MOV WC.hbrBackground, EAX
MOV WC.lpszClassName, OFFSET MainClassName
; INVOKE LoadIcon, InstanceHandle, 500    ; icon ID
; MOV WC.hIcon, EAX
MOV WC.hIcon, NULL
INVOKE LoadCursor, NULL, IDC_ARROW
MOV WC.hCursor, EAX
INVOKE RegisterClassEx, OFFSET WC

INVOKE GetSystemMetrics, SM_CXSCREEN
MOV EDX, $mainWinWidth
CALL CenterDim
MOV wX, EAX
INVOKE GetSystemMetrics, SM_CYSCREEN
MOV EDX, $mainWinHeight
CALL CenterDim
MOV wY, EAX

; Create our main window:
INVOKE CreateWindowEx, WS_EX_OVERLAPPEDWINDOW, OFFSET MainClassName,
OFFSET MainTitleText, $mainWinStyles, wX, wY, $mainWinWidth,
$mainWinHeight, NULL, NULL, InstanceHandle, NULL
MOV MainWinHandle, EAX

; Create the status bar:
INVOKE CreateStatusWindow, $SB_styles, OFFSET NullString, MainWinHandle, $SBID
MOV StatusHandle, EAX
MOV EDX, EAX
; Get the height of the status bar:
INVOKE GetWindowRect, EDX, ADDR gpRect
MOV EAX, gpRect.bottom
SUB EAX, gpRect.top
MOV SBheight, EAX

; Divide status bar into parts:
INVOKE SendMessage, StatusHandle, SB_SETPARTS, $numStatusParts, OFFSET StatusParts

;============= Message loop ===================
msgloop:
INVOKE GetMessage, ADDR msg, NULL, 0, 0
TEST EAX, EAX ;EAX = 0 = exit
JZ exit99
INVOKE TranslateMessage, ADDR msg
INVOKE DispatchMessage, ADDR msg
JMP msgloop

exit99: MOV EAX, msg.wParam
RET

WinMain ENDP

;====================================================================
; Main Window Proc
;====================================================================

MainWindowProc PROC hWin:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL gpRect:RECT

MOV EAX, uMsg
; CMP EAX, WM_COMMAND
; JE do_command
CMP EAX, WM_CREATE
JE do_create
CMP EAX, WM_CLOSE
JE do_close
CMP EAX, WM_SIZE
JE dosize

dodefault:
; use DefWindowProc for all other messages:
INVOKE DefWindowProc, hWin, uMsg, wParam, lParam
RET

;====================================
; WM_COMMAND handler
;====================================
do_command:
; MOV AX, WORD PTR wParam
; CMP AX, ---- ;Check command code here.
; JE ---

do_create:

; Window creation code here

XOR EAX, EAX
RET

;====================================
; WM_SIZE handler
;====================================
dosize:
; Get new size from message:
MOVZX EAX, WORD PTR lParam
MOVZX EDX, WORD PTR lParam + 2
SUB EDX, SBheight
INVOKE MoveWindow, StatusHandle, 0,  EDX, EAX, SBheight, TRUE

XOR EAX, EAX
RET

;====================================
; WM_CLOSE handler
;====================================
do_close:
INVOKE PostQuitMessage, NULL
MOV EAX, TRUE
RET


MainWindowProc ENDP

;====================================================================
; CenterDim
;
; Returns half screen dimension (X or Y) minus half window dimension
;
; On entry,
; EAX = screen dimension
; EDX = window dimension
;
; Returns:
; sdim/2 - wdim/2
;====================================================================

CenterDim PROC

SHR EAX, 1 ;divide screen dimension by 2
SHR EDX, 1 ;divide window dimension by 2
SUB EAX, EDX
RET ;Return w/# in EAX

CenterDim ENDP

END start
Title: Re: Win32 My First Window
Post by: tda0626 on April 25, 2024, 10:20:46 AM
@sudoku Updated source file added to original post

@NoCforMe

Exactly. Once I get the basics of creating a window out of the way, I can just make a default window and pass parameters to it.
Title: Re: Win32 My First Window
Post by: sudoku on April 25, 2024, 10:23:55 AM
Quote from: tda0626 on April 25, 2024, 10:20:46 AM@sudoku Updated source file added to original post
Change your WM_DESTROY to WM_CLOSE... that should do it.
Never mind. That was incorrect. Something else is wrong.
Title: Re: Win32 My First Window
Post by: NoCforMe on April 25, 2024, 10:34:09 AM
Quote from: tda0626 on April 25, 2024, 10:20:46 AMExactly. Once I get the basics of creating a window out of the way, I can just make a default window and pass parameters to it.

Wellll, not quite: you don't really "pass parameters" to a window. Windows does that for you (the standard "window procedure" is <proc name> hWin, uMsg, wParam, lParam). Windows passes the "parameters" to you. Remember, Windows is a message-driven (or event-driven) OS.

What you do is populate your window with other windows, generally Windows "controls":

There are lots more kinds of controls. You would create these as "child windows" of your main window, with the main window as the "parent".

And your window can respond to lots of different events, like the mouse being moved, clicked or double-clicked, keystrokes entered, files being dragged and dropped over the window, lots more.

Oh, and don't forget menus. You can add a menu bar to your window and put all kinds of items in it.

I think you've got a lot of fun ahead of you here.
Title: Re: Win32 My First Window
Post by: tda0626 on April 25, 2024, 10:44:35 AM
Quote from: sudoku on April 25, 2024, 10:23:55 AM
Quote from: tda0626 on April 25, 2024, 10:20:46 AM@sudoku Updated source file added to original post
Change your WM_DESTROY to WM_CLOSE... that should do it.

Yep that did it! Thanks! So happy right now! YAY!  :eusa_dance:
Title: Re: Win32 My First Window
Post by: sudoku on April 25, 2024, 10:52:15 AM
Quote from: tda0626 on April 25, 2024, 10:44:35 AMYep that did it! Thanks! So happy right now! YAY!  :eusa_dance:
:thumbsup:

I think you will do fine.
Now you need to do something a little more interesting with it. :smiley:

added later...
While that may have worked once or twice,, it is not a valid fix for your code.
An error on my part.
Title: Re: Win32 My First Window
Post by: sudoku on April 25, 2024, 11:37:27 AM
I noticed in your new attachment
greeting db "Hello There!"

You need to terminate that string with a zero. Like so...
greeting db "Hello There!", 0
Title: Re: Win32 My First Window
Post by: NoCforMe on April 25, 2024, 11:50:54 AM
If you use my skeleton code, or at least the parts of it that create a status bar at the bottom of the window, you can use the following to display messages there without forcing the user to respond to them as MessageBox() does:

INVOKE  SendMessage, StatusHandle, SB_SETTEXT, 0, <text ptr.>
where "<text ptr.>" is a pointer to the text you want to show (which as Sudoku pointed out must be terminated by a zero--it's known as "ASCIIZ" text, for "ASCII terminated with a zero byte").
Title: Re: Win32 My First Window
Post by: sudoku on April 25, 2024, 12:24:04 PM
At least one of my initial assumptions in the original post here was incorrect.

Upon executing your program in windows 7, the 'busy' cursor is showing, when the arrow cursor should be showing. Something else is amiss that is not obvious at first glance.

I will let the assembly code experts here help you figure out what is causing that effect.

Regarding your use of push/call syntax:

Once you start coding more projects, you might find that using the 'invoke' style of procedure calls is a bit easier. It uses prototypes to aid in detecting the correct number of arguments (will not assemble if incorrect number of arguments), for instance.


Take a look through the examples folder in the masm32 directory. Especially \masm32\examples\exampl01\minimum\minimum.asm (it gives an example of push/call versus 'invoke' style of calling procedures)
Here (its a short code, so I'll post it here)
; #########################################################################

      .386
      .model flat, stdcall
      option casemap :none   ; case sensitive

; #########################################################################

      include \masm32\include\windows.inc
      include \masm32\include\user32.inc
      include \masm32\include\kernel32.inc

      includelib \masm32\lib\user32.lib
      includelib \masm32\lib\kernel32.lib

; #########################################################################

    .code

start:

    jmp @F
      szDlgTitle    db "Minimum MASM",0
      szMsg         db "  --- Assembler Pure and Simple ---  ",0
    @@:

    push MB_OK
    push offset szDlgTitle
    push offset szMsg
    push 0
    call MessageBox

    push 0
    call ExitProcess

    ; --------------------------------------------------------
    ; The following are the same function calls using MASM
    ; "invoke" syntax. It is clearer code, it is type checked
    ; against a function prototype and it is less error prone.
    ; --------------------------------------------------------

    ; invoke MessageBox,0,ADDR szMsg,ADDR szDlgTitle,MB_OK
    ; invoke ExitProcess,0

end start

Most members here use the 'invoke' style. Of course, you can always use what you feel more comfortable with.
Title: Re: Win32 My First Window
Post by: jj2007 on April 25, 2024, 06:09:03 PM
Quote from: tda0626 on April 25, 2024, 08:59:14 AMafter closing the window, it is still showing up in my process list and I have to force close it

It took me a while to find out why. Actually, it has nothing to do with your WM_DESTROY handler - that one is correct, you don't need a WM_CLOSE handler. But your simple...
    call GetMessage
    .break .if !eax
... has a flaw: it doesn't care for errors. Use this message loop, which breaks if GetMessage returns zero or -1. The latter, if not handled, creates the "zombie" that can be killed only via Task Manager. At least, that was what I saw once in the debugger.

Here is a foolproof message loop:

  .While 1
    invoke GetMessage, addr msg, 0, 0, 0
    inc eax
    shr eax, 1
    .Break .if Zero?    ; 0 (OK) or -1 (error)
    invoke TranslateMessage, addr msg
    invoke DispatchMessage, addr msg
  .Endw

P.S.:
Quote from: jj2007 on April 24, 2024, 04:27:10 PMhave a look at this template (https://masm32.com/board/index.php?msg=117713) (93 lines, with a menu, an edit control and a WM_PAINT handler)
Title: Re: Win32 My First Window
Post by: TimoVJL on April 25, 2024, 07:07:26 PM
I have used simple message loop about 25 years
while (GetMessage(&msg, NULL, 0, 0)) {
 TranslateMessage(&msg);
 DispatchMessage(&msg);
}
so, why bother that -1 thing, as if it occurs, there might be programming error.
Perhaps someone show an example of that -1 error.

When will GetMessage return -1? (https://devblogs.microsoft.com/oldnewthing/20130322-00/?p=4873)
Title: Re: Win32 My First Window
Post by: NoCforMe on April 26, 2024, 02:37:49 AM
Timo, can you please post in assembly language here?
Title: Re: Win32 My First Window
Post by: sinsi on April 26, 2024, 03:17:38 AM
jj has it right.
push 0
push 0
push hwnd ;<<<<<<<< bad idea,just pass 0
lea eax, msg
push eax
call GetMessage
The dangers of filtering window messages (https://devblogs.microsoft.com/oldnewthing/20050209-00/?p=36493)
Title: Re: Win32 My First Window
Post by: NoCforMe on April 26, 2024, 04:06:32 AM
Or better yet:
INVOKE GetMessage, ADDR msg, NULL, 0, 0

(same as above, just not push-push-call)
Title: Re: Win32 My First Window
Post by: jj2007 on April 26, 2024, 04:51:49 AM
Here is my personal message loop, merciless optimised for size:

  lea edi, msg
  .While 1
xor eax, eax
invoke GetMessage, edi, eax, eax, eax
inc eax
shr eax, 1
.Break .if Zero? ; 0 (OK) or -1 (error)
invoke TranslateMessage, edi
invoke DispatchMessage, edi
  .Endw
Title: Re: Win32 My First Window
Post by: TimoVJL on April 26, 2024, 08:34:03 PM
Quote from: NoCforMe on April 26, 2024, 02:37:49 AMTimo, can you please post in assembly language here?
why not both
1: #define WIN32_LEAN_AND_MEAN
2: #include <windows.h>
3: MSG msg;
4: void __cdecl WinMainCRTStartup(void)

_WinMainCRTStartup:
  [00000000] 55                    push              ebp
  [00000001] 89E5                  mov              ebp,esp
  [00000003] EB16                  jmp              0000001B
5: {
6:    //MSG msg;
7:    while (GetMessage(&msg, NULL, 0, 0)) {
8:      TranslateMessage(&msg);
  [00000005] 6800000000            push              _msg
  [0000000A] FF1500000000          call              dword ptr [__imp__TranslateMessage@4]
9:      DispatchMessage(&msg);
  [00000010] 6800000000            push              _msg
  [00000015] FF1500000000          call              dword ptr [__imp__DispatchMessageA@4]
  [0000001B] 6A00                  push              0
  [0000001D] 6A00                  push              0
  [0000001F] 6A00                  push              0
  [00000021] 6800000000            push              _msg
  [00000026] FF1500000000          call              dword ptr [__imp__GetMessageA@16]
  [0000002C] 85C0                  test              eax,eax
  [0000002E] 75D5                  jne              00000005
10:    }
11:    ExitProcess(0);
  [00000030] 6A00                  push              0
  [00000032] FF1500000000          call              dword ptr [__imp__ExitProcess@4]
  [00000038] 0F0B                  ud2             
  [0000003A] 89EC                  mov              esp,ebp
  [0000003C] 5D                    pop              ebp
  [0000003D] C3                    ret             
1: #define WIN32_LEAN_AND_MEAN
2: #include <windows.h>
3: MSG msg;
4: void __cdecl WinMainCRTStartup(void)

WinMainCRTStartup:
  [0000000000000000] 4883EC28                     sub               rsp,28
  [0000000000000004] EB24                         jmp               000000000000002A
  [0000000000000006] 90                           nop               
  [0000000000000007] 660F1F840000000000           nop               [rax+rax+0]
5: {
6: //MSG msg;
7: while (GetMessage(&msg, NULL, 0, 0)) {
8: TranslateMessage(&msg);
  [0000000000000010] 488D0D00000000               lea               rcx,[msg]
  [0000000000000017] FF1500000000                 call              qword ptr [__imp_TranslateMessage]
9: DispatchMessage(&msg);
  [000000000000001D] 488D0D00000000               lea               rcx,[msg]
  [0000000000000024] FF1500000000                 call              qword ptr [__imp_DispatchMessageA]
  [000000000000002A] 488D0D00000000               lea               rcx,[msg]
  [0000000000000031] 31D2                         xor               edx,edx
  [0000000000000033] 4531C0                       xor               r8d,r8d
  [0000000000000036] 4531C9                       xor               r9d,r9d
  [0000000000000039] FF1500000000                 call              qword ptr [__imp_GetMessageA]
  [000000000000003F] 85C0                         test              eax,eax
  [0000000000000041] 75CD                         jne               0000000000000010
10: }
11: ExitProcess(0);
  [0000000000000043] 31C9                         xor               ecx,ecx
  [0000000000000045] FF1500000000                 call              qword ptr [__imp_ExitProcess]
  [000000000000004B] 0F0B                         ud2               
  [000000000000004D] 4883C428                     add               rsp,28
  [0000000000000051] C3                           ret               
Title: Re: Win32 My First Window
Post by: NoCforMe on April 27, 2024, 03:16:46 AM
Nice try, but what you posted is disassembly, not assembly.
Don't you ever write assembly language yourself? Or only C?
Title: Re: Win32 My First Window
Post by: TimoVJL on April 27, 2024, 03:39:56 AM
?????
Title: Re: Win32 My First Window
Post by: NoCforMe on April 27, 2024, 05:59:06 AM
What you posted, my friend, was disassembly code of a C program.

Assembly language isn't a read-only language; you can actually write it.

I'm just wondering why you never post any assembly language code that you wrote. In what is an assembly-language forum.
Title: Re: Win32 My First Window
Post by: tda0626 on April 27, 2024, 09:05:37 AM
Quote from: sudoku on April 25, 2024, 11:37:27 AMI noticed in your new attachment
greeting db "Hello There!"

You need to terminate that string with a zero. Like so...
greeting db "Hello There!", 0


The TextOut function, from what I have read, requires that you pass the string size as one of the parameters.
Title: Re: Win32 My First Window
Post by: NoCforMe on April 27, 2024, 09:16:28 AM
You read correctly.

Two ways to handle that: you can either use the length of the string as defined by the assembler (in which case you don't want a zero terminator):
greeting   DB "Hello there!"

INVOKE  TextOut, hDC, x, y, OFFSET greeting, SIZEOF greeting

or you can determine the length of the string dynamically using strlen():
greeting   DB "Hello there!", 0

MOV     EAX, OFFSET greeting
CALL    strlen
INVOKE  TextOut, hDC, x, y, OFFSET greeting, EAX

(I'm using my version of strlen() here which is not the same as the standard function, but you get the idea)

In the second case you'd need to zero-terminate the string, but not in the first case. You'd need to use the 2nd case when you can't determine the length of the string at assemble time (like if you're reading a string from a file or user input).
Title: Re: Win32 My First Window
Post by: sudoku on April 27, 2024, 09:29:41 AM
Quote from: tda0626 on April 27, 2024, 09:05:37 AMThe TextOut function, from what I have read, requires that you pass the string size as one of the parameters.
Okay. I have seen a LOT of assembly source files, I had not come across any that don't use zero terminated strings. As long as you know how to deal with them (strings that are not zero terminated), I guess it is alright.
Title: Re: Win32 My First Window
Post by: tda0626 on April 27, 2024, 10:24:42 AM
Hello everyone again!

With the Push and Calls, I plan on using the higher level invoke statement once I get the bugs out of this program. I wrote it this way as way for me to visually see what was going on. Anyway, that is why. Sorry if it looks like a hot mess.  :biggrin:

I skimmed over Icezelion's tutorial, Tutorial 3: A Simple Window (http://www.interq.or.jp/chubu/r6/masm32/tute/tute003.html) and noticed that there was preliminary code before the call to WinMain

push 0
call GetModuleHandle

mov hinst, eax
call GetCommandLine

mov CommandLine, eax

call WinMain

push eax

Call ExitProcess

so I added that bit of code in there and now the call to CreateWindow fails and trips my error messagebox. Everything in my code looks on par to what he did but don't know why it fails at CreateWindow now. I have attached my new source code to the OP. If someone wants to take a look to see what is going on, it would be much appreciated.

Thanks again for everyone's input! It has been helpful and I am learning new things.

Tim
Title: Re: Win32 My First Window
Post by: TimoVJL on April 27, 2024, 10:47:17 AM
WinMain is just an entry point for languages, that have runtime libraries, like C/C++, Pascal, ...
The WinMain application entry point (https://learn.microsoft.com/en-us/windows/win32/learnwin32/winmain--the-application-entry-point)

TextOut() use string length for showing a part of text from string, if needed.
Windows 1.0 was born in a Pascal world, where string length is part of string, in first byte.
Title: Re: Win32 My First Window
Post by: jj2007 on April 27, 2024, 10:56:22 AM
Quote from: sudoku on April 27, 2024, 09:29:41 AMI had not come across any that don't use zero terminated strings.

Zero terminated strings are a C/C++ thing, not an Assembly feature. Windows takes both C strings (in 98% of cases) and so-called BSTR, i.e. BASIC strings that carry the length information (30h below) in the DWORD before the start of the string:

0041E0DC  30 00 00 00 54 00 68 00 69 00 73 00 20 00 42 00 0...T.h.i.s. .B.
0041E0EC  53 00 54 00 52 00 20 00 68 00 61 00 73 00 20 00 S.T.R. .h.a.s. .
0041E0FC  30 00 78 00 33 00 30 00 20 00 62 00 79 00 74 00 0.x.3.0. .b.y.t.
0041E10C  65 00 73 00 00 00 00 00 00 00 00 00 00 00 00 00 e.s.............

MasmBasic calls them Ole$() (https://www.jj2007.eu/MasmBasicQuickReference.htm#Mb1139):
include \masm32\MasmBasic\MasmBasic.inc
  Init
  mov edi, Ole$("This BSTR has 0x30 bytes")    ; 24 chars=48 bytes
  lea edx, [edi-4]    ; get the address of the length DWORD
  Inkey HexDump$(edx, 64)
EndOfCode

Quote from: TimoVJL on April 27, 2024, 10:47:17 AMin a Pascal world, where string length is part of string, in first byte

A BSTR, as it is used for COM, has the string length in a full DWORD, as shown above. Note it's bytes, not chars, despite of the fact that COM uses Unicode/Utf16. Microsoft won't tell you - see for example the ITextRange::SetText method (https://learn.microsoft.com/en-us/windows/win32/api/tom/nf-tom-itextrange-settext) :cool:

See also SysAllocString (https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-sysallocstring):
BSTR SysAllocString(
  [in, optional] const OLECHAR *psz
);

Note that in contrast to Ole$(...), a sysalloc'ed string must be freed, otherwise you run into memory leaks.
Title: Re: Win32 My First Window
Post by: NoCforMe on April 27, 2024, 11:03:11 AM
Interesting that TextOut() takes the length of a (non-zero-terminated) string as a parameter, while DrawText() can either do the same, or if the string length parameter is set to -1 then expects to see a zero-terminated string.
Title: Re: Win32 My First Window
Post by: sudoku on April 27, 2024, 11:05:57 AM
Quote from: tda0626 on April 27, 2024, 10:24:42 AMI skimmed over Icezelion's tutorial, ...  and noticed that there was preliminary code before the call to WinMain

...
so I added that bit of code in there and now the call to CreateWindow fails

In your current version  (attached again to first post) , you are not pushing the parameters to the stack for WinMain. Now needs 4 parameters, since you added them....

Your current code... see the comments that I added
.386
.model flat, stdcall
option    casemap:none

include    \masm32\include\windows.inc
include    \masm32\include\user32.inc
include    \masm32\include\kernel32.inc
include    \masm32\include\gdi32.inc

includelib    \masm32\lib\user32.lib
includelib    \masm32\lib\kernel32.lib
includelib    \masm32\lib\gdi32.lib

WindowHeight    equ    640
WindowWidth        equ 480



.data

szWindowClass    db "Desktop App",0
szTitle            db "My Window Title",0
create_failed        db "Call to CreateWindow failed!",0
reg_failed        db "Register Window Class failed!",0
mywindow        db "This is my window!",0
greeting        db "Hello There!"
wc    WNDCLASSEX    {0}        ;Allocate Space for Window Class Structure
ps    PAINTSTRUCT    {0}        ;Allocate Space for Paint Structure -- For WM_PAINT
hinst    HINSTANCE ?
CommandLine    LPSTR ?

.code

start:

push 0
call GetModuleHandle

mov hinst, eax
call GetCommandLine

mov CommandLine, eax
; ==========================================================================================
call WinMain  ; <------------ now takes 4 parameters, they need to be pushed to the stack
; ==========================================================================================
push eax

Call ExitProcess
; ================================================================================================
WinMain Proc hinstance:HINSTANCE, hprevinstance:HINSTANCE, lpCmdLine:LPSTR, nCmdShow:dword ; <-- 4 arguments!
; ================================================================================================   
    ;;;;;;;;;;;;;;;;;;;;;;
    ;Extra Local Variables
    ;;;;;;;;;;;;;;;;;;;;;;
   
    local msg:MSG
    local hwnd:HWND
   
    ;;;;;;;;;;;;;;;;;;;;;;;
    ;Window Class Structure
    ;;;;;;;;;;;;;;;;;;;;;;;
   
    mov wc.cbSize, sizeof WNDCLASSEX
    mov wc.style, CS_HREDRAW or CS_VREDRAW
    mov wc.lpfnWndProc, offset WndProc
    mov  wc.cbClsExtra, 0
    mov  wc.cbWndExtra, 0
    push hinst
    pop wc.hInstance
    push IDI_APPLICATION
    push 0
    call LoadIcon
    mov wc.hIcon, eax
    mov wc.hIconSm, eax
    push IDC_ARROW
    push 0
    call LoadCursor
    mov wc.hCursor, eax
    mov wc.hbrBackground, COLOR_WINDOW+1
    mov wc.lpszMenuName, 0
    mov wc.lpszClassName, offset szWindowClass

   
    ;;;;;;;;;;;;;;;;;;;;;;
    ;Register Window Class
    ;;;;;;;;;;;;;;;;;;;;;;
   
        lea eax, wc
        push eax
        call RegisterClassEx
        cmp eax, 0
        jnz RegisterSuccess
   
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;Check for Registration Error
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   
    RegisterFailed:
        push hwnd
        push offset szTitle
        push offset reg_failed
        push hwnd
        call MessageBox
        ret
   
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;If Registration is a success continue
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   
    RegisterSuccess:
   
    ;;;;;;;;;;;;;;;
    ;Create Window
    ;;;;;;;;;;;;;;
   
    push 0
    push hinstance
    push 0
    push 0
    push CW_USEDEFAULT
    push CW_USEDEFAULT
    push CW_USEDEFAULT
    push CW_USEDEFAULT
    push WS_OVERLAPPEDWINDOW or WS_VISIBLE
    lea    eax, mywindow
    push eax
    lea  eax, szWindowClass
    push eax
    push WS_EX_WINDOWEDGE or WS_EX_CLIENTEDGE
    call CreateWindowEx
   
    ;Return Value of CreateWindowEx is a handle to a window -- Move return value into hwnd
   
    mov hwnd, eax
    cmp eax, 0
    jnz CreateSuccess
   
    CreateFailed:
        push hwnd
        push offset szTitle
        push offset create_failed
        push hwnd
        call MessageBox
        ret
   
    CreateSuccess:
       
    ;;;;;;;;;;;;;
    ;Message Loop
    ;;;;;;;;;;;;;   
   
    Message_Loop:
    push 0
    push 0
    push 0
    lea eax, msg
    push eax
    call GetMessage
    cmp eax,0            ;If eax=0 then exit loop
    je End_Loop
    lea eax, msg
    push eax
    call TranslateMessage    ;Process Message
    lea eax, msg
    push eax
    call DispatchMessage        ;Process Message
    jmp Message_Loop            ;Loop back and continue processing messages
   
    End_Loop:
   
    mov eax,msg.wParam
    ret
   
WinMain endp


WndProc Proc hWnd:HWND, umsg:dword, wParam:WPARAM, lParam:LPARAM
   
    ; Define a local variable to a handle to device context
   
    local hdc:HDC
   
    ;;;;;;;;;;;;;;;;;;;;;;;;
    ;Process Window Messages
    ;;;;;;;;;;;;;;;;;;;;;;;;
   
    .if umsg==WM_PAINT
        push offset ps
        push hWnd
        call BeginPaint
        mov hdc, eax
        push lengthof greeting
        push offset greeting
        push TA_CENTER
        push TA_CENTER
        push hdc
        call TextOut            ;Print String Inside Window
        push offset ps
        push hWnd
        call EndPaint
    .elseif umsg==WM_DESTROY
        push 0
        call PostQuitMessage
    .else   
        push lParam
        push wParam
        push umsg
        push hWnd
        call DefWindowProc
    .endif
    xor eax, eax
    ret
 WndProc endp
   



end start
Title: Re: Win32 My First Window
Post by: tda0626 on April 27, 2024, 12:27:42 PM
Do I push them like this?
 

push 0
lea eax, CommandLine
push eax
push 0
push hinst
call WinMain
Title: Re: Win32 My First Window
Post by: NoCforMe on April 27, 2024, 12:39:35 PM
Outside in. Always outside in.

If the function is defined thus:
WinMain    PROC hInstance, hPrevInstance, lpCmdLine, nShowCmd

then you push them in reverse order:

PUSH    nShowCmd
PUSH    lpCmdLine
PUSH    hPrevInstance
PUSH    hInstance

Look, "push-push-call" is SO ugly, and SO error-prone, why don't you just abandon it? Like right now. Get in a better habit. Use INVOKE instead. Don't worry, you won't have to "debug" it. It'll just make life a whole lot easier for you. (You are using MASM, aren't you?)
Title: Re: Win32 My First Window
Post by: TimoVJL on April 27, 2024, 12:42:51 PM
If you don't need commandline, just forget a whole that WinMain and create window in normal way.
As many of these site users can tell, WinMain don't belong to asm world.
Commandline processing is just own story and some of can give details for that, like Vortex.
Naturally i can provide that in C language, as know how avoid crt.


Wanking should be fun. That's why I do it.
Title: Re: Win32 My First Window
Post by: NoCforMe on April 27, 2024, 01:05:28 PM
@Timo:
Your post is 100% correct, and it gave me a chuckle on top of that.
Title: Re: Win32 My First Window
Post by: C3 on April 27, 2024, 08:29:15 PM
Quote from: TimoVJL on April 27, 2024, 12:42:51 PMIf you don't need commandline, just forget a whole that WinMain and create window in normal way.
As many of these site users can tell, WinMain don't belong to asm world.
Commandline processing is just own story and some of can give details for that, like Vortex.
Naturally i can provide that in C language, as know how avoid crt.

Or then I am just too lazy to change the entry point in Visual Studio and use what it defaults in Assembly Builds.

For Window Projects:

WinMainCRTStartup PROC
    GdiPlusBegin
    mov hInstance,rvcall(GetModuleHandle,NULL)
    mov CommandLine,rvcall(GetCommandLine)
    rcall WinMain,hInstance,NULL,CommandLine,SW_SHOWDEFAULT
    GdiPlusEnd
    rcall ExitProcess,0
    ret
WinMainCRTStartup ENDP


And for DLL Projects:
_DllMainCRTStartup PROC hInstDLL:QWORD,reason:QWORD,unused:QWORD

    .if reason == DLL_PROCESS_ATTACH
        mrm hInstance, hInstDLL

        mov rax, TRUE ; Flag to Start the Main Program

    .elseif reason == DLL_PROCESS_DETACH

    .elseif reason == DLL_THREAD_ATTACH

    .elseif reason == DLL_THREAD_DETACH

    .endif

    ret

_DllMainCRTStartup ENDP

There is also default entry point for Console Projects in Visual Studio.
Title: Re: Win32 My First Window
Post by: TimoVJL on April 27, 2024, 10:00:16 PM
Quote from: C3 on April 27, 2024, 08:29:15 PMOr then I am just too lazy to change the entry point in Visual Studio and use what it defaults in Assembly Builds.

There is also default entry point for Console Projects in Visual Studio.
I also use those linker's default function names, as link.exe and polink.exe knows them.
Title: Re: Win32 My First Window
Post by: tda0626 on April 27, 2024, 11:05:20 PM
Quote from: TimoVJL on April 27, 2024, 12:42:51 PMIf you don't need commandline, just forget a whole that WinMain and create window in normal way.
As many of these site users can tell, WinMain don't belong to asm world.
Commandline processing is just own story and some of can give details for that, like Vortex.
Naturally i can provide that in C language, as know how avoid crt.



I was going by the tutorial so wanted to follow it verbatim.



Pulled up my program in a debugger, x32dbg, to see if I saw anything that looked suspicious but didn't find anything. Not sure what is going on...
Title: Re: Win32 My First Window
Post by: sudoku on April 27, 2024, 11:17:14 PM
Quote from: tda0626 on April 27, 2024, 11:05:20 PMI was going by the tutorial so wanted to follow it verbatim.
....
Pulled up my program in a debugger, x32dbg, to see if I saw anything that looked suspicious but didn't find anything. Not sure what is going on...

Are you sure that you haven't missed anything during conversion, assuming you converted it from 'invoke' to 'push/push... call'? ( Is your current project iczelions tutorial #3?)

And pushed the parameters in the correct order?

And have the correct number of parameters pushed to the stack? (For a given call)

Tip: You can post your attachment in your next post. Not necessary to keep using the first post, it could get overlooked if attached there. Seems I was the only one that downloaded the last attachment. (At the time of this post, downloads = 1)

Besides when posting your code sequentially within your posts (instead of overwriting previous attachments) you have a record of the progress of your project. As well as making it easier for those that are following your progress.
When the project is completed and bug free, you can always remove the older attachments since they would be no longer needed. My point of view.
Title: Re: Win32 My First Window
Post by: sudoku on April 28, 2024, 12:46:55 AM
Iczelions tutorial #3 my conversion from 'invoke' syntax to 'push/call' syntax. Compare that with your own code, tda0626.  :smiley:

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

.data
ClassName db "SimpleWinClass",0
AppName  db "Our First Window",0

.data?
hInstance dd ?
CommandLine dd ?

.code
start:
    push 0
    call GetModuleHandle
    mov    hInstance,eax
   
    call GetCommandLine
    mov  CommandLine,eax
   
    push SW_SHOWDEFAULT
    push CommandLine
    push 0
    push hInstance
    call WinMain
   
    push eax
    call ExitProcess

WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
LOCAL wc:WNDCLASSEX, msg:MSG, hwnd:HWND
   
    mov   wc.cbSize, SIZEOF WNDCLASSEX
    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 0
    call LoadIcon
    mov   wc.hIcon, eax
    mov   wc.hIconSm, eax
   
    push IDC_ARROW
    push 0
    call LoadCursor
    mov   wc.hCursor, eax
   
    lea eax, wc
    push eax
    call RegisterClassEx
   
    push 0
    push hInst
    push 0
    push 0
    push CW_USEDEFAULT
    push CW_USEDEFAULT
    push CW_USEDEFAULT
    push CW_USEDEFAULT
    push WS_OVERLAPPEDWINDOW
    push offset AppName
    push offset ClassName
    push 0
    call CreateWindowEx
    mov   hwnd, eax
   
    push SW_SHOWNORMAL
    push hwnd
    call ShowWindow
   
    push hwnd
    call UpdateWindow
    .WHILE TRUE
   
        push 0
        push 0
        push 0
        lea eax, msg
        push eax
        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 0
        call PostQuitMessage
       
    .else
   
        push lParam
        push wParam
        push uMsg
        push hWnd
        call DefWindowProc
            
        ret
    .endif
   
    xor eax,eax
    ret
WndProc endp

end start
Title: Re: Win32 My First Window
Post by: sinsi on April 28, 2024, 12:48:47 AM
Quote from: TimoVJL on April 27, 2024, 12:42:51 PMIf you don't need commandline, just forget a whole that WinMain and create window in normal way.
As many of these site users can tell, WinMain don't belong to asm world.
Agreed.

    .else   
        push lParam
        push wParam
        push umsg
        push hWnd
        call DefWindowProc
    .endif
          xor eax, eax ;<<<<<<<<< this will ALWAYS return 0, not a good thing
    ret
Title: Re: Win32 My First Window
Post by: tda0626 on April 28, 2024, 02:05:31 AM
Quote from: sudoku on April 28, 2024, 12:46:55 AMIczelions tutorial #3 my conversion from 'invoke' syntax to 'push/call' syntax. Compare that with your own code, tda0626.  :smiley:

Awesome! I made some minor modifications to my code based of yours and now it runs and exits without a hitch! The call to CreateWindow now returns a value in EAX when before it was returning zero.  :eusa_clap:

Now I will convert it over to invoke statements to shorten it.

Thank you for the help!

Title: Re: Win32 My First Window
Post by: sudoku on April 28, 2024, 02:10:46 AM
Quote from: tda0626 on April 28, 2024, 02:05:31 AMI made some minor modifications to my code based of yours and now it runs and exits without a hitch!
...
Now I will convert it over to invoke statements to shorten it.
Great!  :thumbsup:

Quote from: tda0626 on April 28, 2024, 02:05:31 AMThank you for the help!
No problem.  :smiley:

Converting the code as I did, is often error prone itself. I had to make several corrections while converting it (due to my own errors). I hope that I won't have to do that again any time soon, especially with larger projects.  :greensml:
Title: Re: Win32 My First Window
Post by: tda0626 on April 28, 2024, 02:41:56 AM
2 questions still:

Running the program in debugger, I noticed that my jnz code is jne. Are these the same thing?

Could you explain a little bit in detail what is happening here and why we use msg.wParam?

mov eax,msg.wParam

I am assuming the wParam is storing the return values of a message. That is how I am reading it.


Tim
Title: Re: Win32 My First Window
Post by: NoCforMe on April 28, 2024, 03:00:33 AM
1. JNZ == JNE (just as JZ == JE)**. They're just assembler synonyms.

2. MSG is a structure, and wParam is one of its members. Not sure why that's being used as the source of the return value from WinMain(), but apparently Iczelion had his reasons for doing that.

BTW, the only time you care what an executable program returns (as that return value from WinMain()) is when you're running a program from a batch file and need to check its return status. Otherwise, as suggested above, don't bother with the whole WinMain() business, which is really for C and other higher-level languages. It's a needless construct in assembly.

** HOWEVER: JB/JBE is not the same as JL/JLE, and JA/JAE is not the same as JG/JGE. One set is for unsigned comparisons, the other for signed. Be sure you use the right one!
Title: Re: Win32 My First Window
Post by: sudoku on April 28, 2024, 03:08:40 AM
Quote from: tda0626 on April 28, 2024, 02:41:56 AM2 questions still:

Running the program in debugger, I noticed that my jnz code is jne. Are these the same thing?
Yes. jnz = Jump if not zero (result of a comparison)
    jne = Jump if not equal(result of a comparison)

The debuggers do that, but only for instructions that are equivalent. ollydbg does the same.
Quote from: tda0626 on April 28, 2024, 02:41:56 AMCould you explain a little bit in detail what is happening here and why we use msg.wParam?

mov eax,msg.wParam
I am assuming the wParam is storing the return values of a message. That is how I am reading it.
More specifically, msg.wParam is the wParam part of the MSG structure
From win32.hlp...
The MSG structure contains message information from a thread's message queue.

typedef struct MSG {    // msg 
    HWND  hwnd;   
    UINT  message;
    WPARAM wParam;
    LPARAM lParam;
    DWORD  time;
    POINT  pt;
} MSG;
Members:

hwnd
Identifies the window whose window procedure receives the message.

message
Specifies the message number.

wParam
Specifies additional information about the message. The exact meaning depends on the value of the message member.

lParam
Specifies additional information about the message. The exact meaning depends on the value of the message member.

time
Specifies the time at which the message was posted.

pt
Specifies the cursor position, in screen coordinates, when the message was posted.
Title: Re: Win32 My First Window
Post by: sinsi on April 28, 2024, 03:51:57 AM
Quote from: tda0626 on April 28, 2024, 02:05:31 AMThe call to CreateWindow now returns a value in EAX when before it was returning zero.
But do you know why? There were (I think) two problems with your latest code, neither of which were because you weren't using invoke
Title: Re: Win32 My First Window
Post by: tda0626 on April 28, 2024, 04:18:02 AM
Quote from: sinsi on April 28, 2024, 03:51:57 AM
Quote from: tda0626 on April 28, 2024, 02:05:31 AMThe call to CreateWindow now returns a value in EAX when before it was returning zero.
But do you know why? There were (I think) two problems with your latest code, neither of which were because you weren't using invoke


I am not so sure why it works now and not before. I only changed a few lines of code per sudoku's code. Can you give me a rundown on the two problems that were in it?


Quote from: sudoku on April 28, 2024, 03:08:40 AMMore specifically, msg.wParam is the wParam part of the MSG structure
From win32.hlp...

I understand that piece but why return the value? Is wParam a return value from a message?
Title: Re: Win32 My First Window
Post by: NoCforMe on April 28, 2024, 04:20:33 AM
Quote from: sudoku on April 28, 2024, 03:08:40 AMYes. jnz = Jump if not zero (result of a comparison)
    jne = Jump if not equal(result of a comparison)

Hmm, is there an echo in here?
Title: Re: Win32 My First Window
Post by: sudoku on April 28, 2024, 04:32:10 AM
Quote from: tda0626 on April 28, 2024, 04:18:02 AM
Quote from: sudoku on April 28, 2024, 03:08:40 AMMore specifically, msg.wParam is the wParam part of the MSG structure
From win32.hlp...
I understand that piece but why return the value? Is wParam a return value from a message?

Short answer, idunno for certain. Convention?
Longer answer via Google search...
https://www.gamedev.net/forums/topic/121603-why-must-return-msgwparam/ (https://www.gamedev.net/forums/topic/121603-why-must-return-msgwparam/)
and another...
https://cplusplus.com/forum/windows/103120/ (https://cplusplus.com/forum/windows/103120/)
... still another
https://forums.codeguru.com/showthread.php?497342-Significance-of-return- (https://forums.codeguru.com/showthread.php?497342-Significance-of-return-)
and ...
https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-winmain
I learned a little bit from those, although mostly they are geared towards c/c++ programmers it seems.

Quote from: NoCforMe on April 28, 2024, 04:20:33 AMHmm, is there an echo in here?
No.
I was preparing my post, while you were posting. (I'm slow)
Title: Re: Win32 My First Window
Post by: NoCforMe on April 28, 2024, 05:17:05 AM
Some 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.
Title: Re: Win32 My First Window
Post by: sinsi on April 28, 2024, 05:18:14 AM
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:
Title: Re: Win32 My First Window
Post by: NoCforMe on April 28, 2024, 05:25:26 AM
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.
Title: Re: Win32 My First Window
Post by: tda0626 on April 28, 2024, 12:17:56 PM
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!
Title: Re: Win32 My First Window
Post by: NoCforMe on April 28, 2024, 01:11:42 PM
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:

(https://i.postimg.cc/5XTdpkgz/wParam.jpg) (https://postimg.cc/5XTdpkgz)

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 (https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-setfont)--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 (https://learn.microsoft.com/en-us/windows/win32/controls/wm-ctlcolorstatic))

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!)
Title: Re: Win32 My First Window
Post by: jj2007 on April 28, 2024, 06:40:48 PM
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 (http://www.ollydbg.de/version2.html)), 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.
Title: Re: Win32 My First Window
Post by: NoCforMe on April 29, 2024, 11:23:07 AM
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().)
Title: Re: Win32 My First Window
Post by: 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?
Title: Re: Win32 My First Window
Post by: daydreamer on April 30, 2024, 04:18:52 AM
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


Title: Re: Win32 My First Window
Post by: NoCforMe on April 30, 2024, 05:41:21 AM
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
Title: Re: Win32 My First Window
Post by: jj2007 on April 30, 2024, 06:11:57 AM
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:
Title: Re: Win32 My First Window
Post by: tda0626 on May 01, 2024, 08:18:51 AM
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
Title: Re: Win32 My First Window
Post by: jj2007 on May 01, 2024, 08:45:22 AM
      invoke BeginPaint, hWnd, addr ps
      mov hdc, eax
      invoke GetClientRect, hWnd, addr rect
      invoke DrawText, hdc, offset greeting, ...
Title: Re: Win32 My First Window
Post by: tda0626 on May 01, 2024, 08:53:56 AM
Thanks JJ. I feel so stupid  :joking:
Title: Re: Win32 My First Window
Post by: 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:
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, ...
Title: Re: Win32 My First Window
Post by: tda0626 on May 01, 2024, 10:21:52 AM
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!

Title: Re: Win32 My First Window
Post by: Greenhorn on May 01, 2024, 01:52:04 PM
Quote from: tda0626 on May 01, 2024, 10:21:52 AM
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!



Size the window and look what happens.  :toothy:

Add this to solve the issue.

CASE WM_SIZE
     invoke InvalidateRect, hWnd, NULL, TRUE
     xor  eax, eax
     ret
Title: Re: Win32 My First Window
Post by: NoCforMe on May 01, 2024, 02:30:30 PM
Yes; to explain what's going on here:
The rectangle passed in the PAINTSTRUCT structure obtained by BeginPaint() contains the area of the window that needs to be painted. This area can be the entire window, or it can be some smaller area.

The way to force a window to repaint itself is a little non-intuitive: you call InvalidateRect() to "invalidate" the contents of the window, which makes the system send the window a WM_PAINT message.

One of the parameters of InvalidateRect() (the 2nd one) is a pointer to a RECT. If you pass NULL here, as Greenhorn showed, then the entire window is repainted (the RECT that gets retrieved by BeginPaint() covers the entire client area of the window).

Since I don't have a working executable of this program, I'm curious: what does happen if you resize the window?
Title: Re: Win32 My First Window
Post by: jj2007 on May 01, 2024, 06:23:20 PM
Quote from: Greenhorn on May 01, 2024, 01:52:04 PMSize the window and look what happens.  :toothy:

Add this to solve the issue.

CASE WM_SIZE
     invoke InvalidateRect, hWnd, NULL, TRUE
     xor  eax, eax
     ret

The program works perfectly and redraws the text in the centre when you size the window. Can you explain which "issue" you want to solve by "processing" WM_SIZE with an InvalidateRect?

Quote from: NoCforMe on May 01, 2024, 02:30:30 PMwhat does happen if you resize the window?

Exactly what is supposed to happen: the window redraws itself.

The only quirk when using ps.rcPaint instead of rect is that when you move the window around, so that part of it goes outside the desktop, then the text gets painted elsewhere, i.e. it won't be centred. This is because WM_PAINT passes a RECT that is smaller than what you get with GetWindowRect.

However, it gets repainted correctly as soon as you size the window: the default WM_SIZE processing takes care of that. No need for a separate handler.

Test yourself:

CASE WM_PAINT
mov hdc, rv(BeginPaint, hWnd, addr ps)
if 1 ; put if 0 to test the second option
invoke GetClientRect, hWnd, addr rect
invoke DrawText, hdc, offset greeting, -1, addr rect, DT_SINGLELINE or DT_CENTER or DT_VCENTER
else
invoke DrawText, hdc, offset greeting, -1, addr ps.rcPaint, DT_SINGLELINE or DT_CENTER or DT_VCENTER
endif
invoke EndPaint, hWnd, addr ps
ret
Title: Re: Win32 My First Window
Post by: tda0626 on May 01, 2024, 09:10:25 PM
Quote from: Greenhorn on May 01, 2024, 01:52:04 PM
Quote from: tda0626 on May 01, 2024, 10:21:52 AM
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!



Size the window and look what happens.  :toothy:

Add this to solve the issue.

CASE WM_SIZE
     invoke InvalidateRect, hWnd, NULL, TRUE
     xor  eax, eax
     ret


Wouldn't the DefWindowProc take care of those messages and invalidate the window?
Title: Re: Win32 My First Window
Post by: jj2007 on May 01, 2024, 10:10:27 PM
Quote from: tda0626 on May 01, 2024, 09:10:25 PMWouldn't the DefWindowProc take care of those messages and invalidate the window?

Exactly.
Title: Re: Win32 My First Window
Post by: Greenhorn on May 02, 2024, 07:31:23 AM
Quote from: jj2007 on May 01, 2024, 06:23:20 PMThe program works perfectly and redraws the text in the centre when you size the window. Can you explain which "issue" you want to solve by "processing" WM_SIZE with an InvalidateRect?


No, it does not work perfectly. And yes, I've tested it.
The DefWindowProc does not invalidate the whole client area by default, why should it do that?

In case of ".if 1" and sizing the window (smaller/bigger) ...
invoke GetClientRect, hWnd, addr rect
invoke DrawText, hdc, offset greeting, -1, addr rect, DT_SINGLELINE or DT_CENTER or DT_VCENTER
(https://i.postimg.cc/fkgQz24P/size-smaller.jpg) (https://postimg.cc/fkgQz24P)(https://i.postimg.cc/B8r8dzRD/size-bigger1.jpg) (https://postimg.cc/B8r8dzRD)


In case of ".if 0" and sizing the window (bigger) ...
invoke DrawText, hdc, offset greeting, -1, addr ps.rcPaint, DT_SINGLELINE or DT_CENTER or DT_VCENTER

(https://i.postimg.cc/grJJgmx5/size-bigger.jpg) (https://postimg.cc/grJJgmx5)

The areas within the red rectangles need to be drawn.

NoCforMe pointed to what happens in WM_PAINT. Only the part of the client area that needs to be (re)drawn gets invalidated.
If you size the window smaller, then no repaint is needed and no WM_PAINT is sent to the window.
If you size the window bigger, then ps.rcPaint contains just the parts that needs to be drawn (left or bottom). The rest of the client area remains validated.

That is the reason why you have to invalidate the whole client area when sizing the window in this case.
Title: Re: Win32 My First Window
Post by: jj2007 on May 02, 2024, 07:55:48 AM
Quote from: Greenhorn on May 02, 2024, 07:31:23 AMThe DefWindowProc does not invalidate the whole client area by default, why should it do that?

I understand your logic, but as a matter of fact, when sizing the window, the text moves to the new centre. At least on my machine (Windows 10) :cool:

The effect of partial invalidation becomes evident only when part of the window is outside the desktop's client area. Then, when sizing, the text moves to the centre of the visible part - as expected.

The easiest solution to get the intended behaviour is indeed GetClientRect.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 02, 2024, 08:20:03 AM
Quote from: jj2007 on May 02, 2024, 07:55:48 AMThe easiest solution to get the intended behaviour is indeed GetClientRect.
Yes, agreed. That way you're always repainting the entire client area.

So cancel my suggested "refinement".
Title: Re: Win32 My First Window
Post by: Greenhorn on May 02, 2024, 09:48:47 AM
Quote from: jj2007 on May 02, 2024, 07:55:48 AMI understand your logic, but as a matter of fact, when sizing the window, the text moves to the new centre. At least on my machine (Windows 10) :cool:

That is because the OP uses CS_HREDRAW or CS_VREDRAW as window class style.
My testing application didn't, so, my fault.  :wink2:

However, the WndProc should be changed.
I'm not familiar with the MASM SDK macros but isn't there something like CASE ELSE ?
The DefWindowProc is called always but shouldn't.
And the code still does not return a proper value after handling a message.

WndProc proc hWnd:HWND, message:UINT, wParam:WPARAM, lParam:LPARAM   
   
    local hdc:HDC
    local ps:PAINTSTRUCT
    local rect:RECT
   
    SWITCH message
           
    CASE WM_PAINT
   
        invoke BeginPaint, hWnd, addr ps
        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   ; should return zero here
   
    CASE WM_DESTROY
   
        invoke PostQuitMessage, 0
        ret   ; should return zero here
       
    ENDSW
   
        invoke DefWindowProc, hWnd, message, wParam, lParam   ; This is executed always !
       
        ret

WndProc endp
Title: Re: Win32 My First Window
Post by: jj2007 on May 02, 2024, 10:44:13 AM
Quote from: Greenhorn on May 02, 2024, 09:48:47 AMThe DefWindowProc is called always but shouldn't.

Quote from: jj2007 on April 28, 2024, 06:40:48 PMThere 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.
Title: Re: Win32 My First Window
Post by: jj2007 on May 02, 2024, 11:00:35 AM
Quote from: Greenhorn on May 02, 2024, 09:48:47 AMinvoke EndPaint, hWnd, addr ps
        ret   ; should return zero here

You can return zero, but you can also just continue with DefWindowProc:

invoke GetUpdateRect, hWnd, addr rc, 0
before BeginPaint
rc.left         0
rc.top          0
rc.right        584
rc.bottom       341

invoke GetUpdateRect, hWnd, addr rc, 0
after EndPaint
rc.left         0
rc.top          0
rc.right        0
rc.bottom       0

After EndPaint, there is nothing to update, so DefWindowProc will do nothing. You may save a few cycles by returning, though :cool:
Title: Re: Win32 My First Window
Post by: sinsi on May 02, 2024, 11:21:40 AM
Quote from: jj2007 on May 02, 2024, 11:00:35 AMAfter EndPaint, there is nothing to update, so DefWindowProc will do nothing. You may save a few cycles by returning, though
Now, now, jj, you should always obey Microsoft  :eusa_naughty:
Quote from: WM_PAINTAn application returns zero if it processes this message.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 02, 2024, 01:21:12 PM
OK, here's how that window-message processing should go. No need for code: use macros, don't use macros, whatever, but make it do this:

(https://i.postimg.cc/CBWv51kv/Window-message-processing.gif) (https://postimg.cc/CBWv51kv)

Dang it! How do I get images to display larger so you don't have to click on them? I used the "Do not resize my image" option and it still resized it! I know it's possible because I've seen others post larger images.
Title: Re: Win32 My First Window
Post by: jj2007 on May 02, 2024, 05:42:49 PM
Quote from: sinsi on May 02, 2024, 11:21:40 AMNow, now, jj, you should always obey Microsoft  :eusa_naughty:
QuoteAn application returns zero if it processes this message.
Win32 Programmer's Reference, version 50.1.7600.16386:
QuoteAn application should return zero if it processes this message
:smiley:

QuoteThe BeginPaint function (https://learn.microsoft.com/en-gb/windows/win32/api/winuser/nf-winuser-beginpaint) automatically sets the clipping region of the device context to exclude any area outside the update region. The update region is set by the InvalidateRect or InvalidateRgn function and by the system after sizing, moving, creating, scrolling, or any other operation that affects the client area. If the update region is marked for erasing, BeginPaint sends a WM_ERASEBKGND message to the window.

An application should not call BeginPaint except in response to a WM_PAINT message. Each call to BeginPaint must have a corresponding call to the EndPaint function.

If the caret is in the area to be painted, BeginPaint automatically hides the caret to prevent it from being erased.

If the window's class has a background brush, BeginPaint uses that brush to erase the background of the update region before returning.

BeginPaint is a very versatile function. What they don't say explicitly is that immediately after the invoke BeginPaint the client area is already validated. So, if instead of returning zero you let DefWindowProc kick in, it will see "nothing to do" and just return.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 02, 2024, 06:12:46 PM
But why do that? Sounds like slightly perverse programming to me: just do it like they say to do it and you'll be fine. Why do you want to include DefWindowProc() in that?

Some rules are made to be broken. But then, some rules are better followed ...
Title: Re: Win32 My First Window
Post by: TimoVJL on May 02, 2024, 06:41:12 PM
Many of us remember this, from Petzold book:
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC        hdc ;
    PAINTSTRUCT ps ;
    RECT        rect ;
   
    switch (message)
    {
    case WM_CREATE:
          PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ;
          return 0 ;

    case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
         
          GetClientRect (hwnd, &rect) ;
         
          DrawText (hdc, TEXT ("Hello, Windows 98!"), -1, &rect,
                    DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
          EndPaint (hwnd, &ps) ;
          return 0 ;
         
    case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
}
HelloWin petzold-pw5e (https://github.com/recombinant/petzold-pw5e/blob/master/Chapter%2003%20Windows%20and%20Messages/01%20HelloWin/hellowin.c)
Title: Re: Win32 My First Window
Post by: jj2007 on May 02, 2024, 06:42:03 PM
See reply #64, case 2 or 3a:

Quote from: jj2007 on April 28, 2024, 06:40:48 PM2. 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.

Here it is case 2: no further processing needed. You could use case 3a, though, because DefWindowProc calls BeginPaint internally, and then finds out that there is no update rectangle. So it returns. Lots of cycles wasted but no harm done.

Using 3a, i.e. continuing to DefWindowProc, will save a few bytes (9 with uses esi edi ebx, 6 without uses) because ret is a macro. The downside is some unnecessary processing, which costs around 40 microseconds :cool:

Quote from: TimoVJL on May 02, 2024, 06:41:12 PMcase WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
Same case: you can return zero, but you don't have to. DefWindowProc will do nothing harmful, and you saved 6 or 9 bytes, depending on your uses clause :cool:
Title: Re: Win32 My First Window
Post by: NoCforMe on May 03, 2024, 04:44:15 AM
Quote from: jj2007 on May 02, 2024, 06:42:03 PM
Quote from: TimoVJL on May 02, 2024, 06:41:12 PMcase WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
Same case: you can return zero, but you don't have to. DefWindowProc will do nothing harmful, and you saved 6 or 9 bytes, depending on your uses clause :cool:
Yes, you can call DefWindowProc(), but again: why would you want to? Just for the sake of your nice pretty macro-formatted code?

And saving 6 or 9 bytes of code? "Woohoo, my code is 9 bytes smaller! Only, sometimes it does weird things ...".
Title: Re: Win32 My First Window
Post by: jj2007 on May 03, 2024, 04:54:09 AM
Quote from: NoCforMe on May 03, 2024, 04:44:15 AMwhy would you want to?

Why would you not want to, knowing that it's shorter and costs less than 50 microseconds? Why do you insist to return, instead of letting Windows do its job?

And no, it doesn't do weird things. It behaves like any other window, and that's by design.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 03, 2024, 04:59:05 AM
Wellll, I don't know about you, but this is one case where if Micro$oft is telling me I should return 0 and not call DefWindowProc(), that's what I'm gonna do. You, of course, are free to do whatever you like.
Title: Re: Win32 My First Window
Post by: TimoVJL on May 03, 2024, 05:25:27 AM
Everyone are free to make experiments and others decide, what programs they use.
The new world order for programming isn't suitable for everyone.
Of course it's a bit boring to use same program template for all programs and no one tell to you, that you are a genius, but if do the job, what worry about.
A programs are only important, not just a programmer, who wrote them.
Title: Re: Win32 My First Window
Post by: tda0626 on May 03, 2024, 05:34:23 AM
Quote from: TimoVJL on May 02, 2024, 06:41:12 PMMany of us remember this, from Petzold book:
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC        hdc ;
    PAINTSTRUCT ps ;
    RECT        rect ;
   
    switch (message)
    {
    case WM_CREATE:
          PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ;
          return 0 ;

    case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
         
          GetClientRect (hwnd, &rect) ;
         
          DrawText (hdc, TEXT ("Hello, Windows 98!"), -1, &rect,
                    DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
          EndPaint (hwnd, &ps) ;
          return 0 ;
         
    case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
}
HelloWin petzold-pw5e (https://github.com/recombinant/petzold-pw5e/blob/master/Chapter%2003%20Windows%20and%20Messages/01%20HelloWin/hellowin.c)

Yep, I have a copy of this book and translated his code over to assembler for practice.

Tim
Title: Re: Win32 My First Window
Post by: NoCforMe on May 03, 2024, 05:41:10 AM
Quote from: tda0626 on May 03, 2024, 05:34:23 AMYep, I have a copy of this book and translated his code over to assembler for practice.
Care to show us your translation so we can tear it apart and critique it?

Yep, Petzold is good stuff. Old, but timeless.
Title: Re: Win32 My First Window
Post by: tda0626 on May 03, 2024, 05:48:47 AM
Quote from: NoCforMe on May 03, 2024, 05:41:10 AM
Quote from: tda0626 on May 03, 2024, 05:34:23 AMYep, I have a copy of this book and translated his code over to assembler for practice.
Care to show us your translation so we can tear it apart and critique it?

Yep, Petzold is good stuff. Old, but timeless.

Should have been more specific, I translated only the "A Window of ones own", which is attached to post on page 5 I think.

Looked at his text metrics code and besides the custom include file he wants you to import into the program, I have no clue to translating some of his c code in that section. Might just skip that section and come back to it later.

Tim

Title: Re: Win32 My First Window
Post by: NoCforMe on May 03, 2024, 06:58:51 AM
Quote from: tda0626 on May 03, 2024, 05:48:47 AMLooked at his text metrics code and besides the custom include file he wants you to import into the program, I have no clue to translating some of his c code in that section.
What program is that exactly? I have his book (yes, actual book, the size of a boat anchor) in front of me. I could take a look at that code.
Title: Re: Win32 My First Window
Post by: jj2007 on May 03, 2024, 07:25:17 AM
Quote from: NoCforMe on May 03, 2024, 04:59:05 AMWellll, I don't know about you, but this is one case where if Micro$oft is telling me I should return 0 and not call DefWindowProc(), that's what I'm gonna do. You, of course, are free to do whatever you like.

You, too :thumbsup:
Title: Re: Win32 My First Window
Post by: tda0626 on May 03, 2024, 10:03:08 AM
Quote from: NoCforMe on May 03, 2024, 06:58:51 AM
Quote from: tda0626 on May 03, 2024, 05:48:47 AMLooked at his text metrics code and besides the custom include file he wants you to import into the program, I have no clue to translating some of his c code in that section.
What program is that exactly? I have his book (yes, actual book, the size of a boat anchor) in front of me. I could take a look at that code.

In the 5th edition, it is on page 73. The custom include file starts on page 70.
Title: Re: Win32 My First Window
Post by: tda0626 on May 03, 2024, 10:17:22 AM
So my brother is also learning assembly and practicing on his own and asked me to debug his source code.He wanted to try to convert some C code of a window example on Microsoft's site to see where there were gaps in his knowledge and for practice. Anyway, I looked over his code and caught some little mistakes he did and commented them. But after running it, CreateWindow returns 0 and triggers the error code. I ran it in Ollydbg and didn't see any apparent issues so I thought I would post his source here and see if anyone spotted anything. It is attached to this post.

Last weekend, I went over to his house and we went over a window program together explaining every step in detail while he was the one inputting all the code. He is new to assembler and never really messed with it but he is picking it up alright I think.
Title: Re: Win32 My First Window
Post by: sudoku on May 03, 2024, 10:32:25 AM
Check the window class name is correct when calling CreateWindowEx.... you have the wrong string there "szAppName"

should be "szWindowClass"  :icon_idea:

    invoke CreateWindowExA,\
        WS_EX_OVERLAPPEDWINDOW,\
        offset szAppName,\                                ;;;; should be the registered class name
        offset win_caption,\
        WS_OVERLAPPEDWINDOW ,\
        CW_USEDEFAULT,\
        CW_USEDEFAULT,\
        CW_USEDEFAULT,\
        CW_USEDEFAULT,\
        0,\
        0,\
        hInstance,\
        0

mov wcex.lpszClassName, offset szWindowClass  ;;;; the registered class name
Tip: for invoke statements we use 'addr' not 'offset', generally.
also in .data there are a couple variables with '?' ought to be '0' (for initialized data)
and the structures more commonly -->  "ps PAINTSTRUCT <0>"  (where  "ps PAINTSTRUCT {0}" is in your source)
Title: Re: Win32 My First Window
Post by: tda0626 on May 03, 2024, 10:55:46 AM
See that is why I come here, you guys are awesome and fast!  :biggrin:

Thanks Sudoku!
Title: Re: Win32 My First Window
Post by: NoCforMe on May 03, 2024, 10:56:43 AM
Quote from: sudoku on May 03, 2024, 10:32:25 AMTip: for invoke statements we use 'addr' not 'offset', generally.
"We"? Who is the "we" you refer to here? Certainly not me.

Use OFFSET or ADDR depending:
When you use ADDR, the assembler inserts the following code:
  LEA  EAX, <variable>
OFFSETs are treated as constant values, just like any other number.
Title: Re: Win32 My First Window
Post by: sudoku on May 03, 2024, 10:58:24 AM
Quote from: tda0626 on May 03, 2024, 10:55:46 AMSee that is why I come here, you guys are awesome and fast!  :biggrin:

Thanks Sudoku!
Not a problem.  :smiley:

NoCforMe, I never use offset in an invoke statement. And I have rarely seen any other source code with it used in that fashion. It may exist somewhere but it is much less common. Thats all I have to say about that.  :smiley:
https://masm32.com/board/index.php?msg=53727
Title: Re: Win32 My First Window
Post by: NoCforMe on May 03, 2024, 11:06:03 AM
    INVOKE    SendMessage, hWin, WM_SETTEXT, 0, OFFSET <text2set>

Nothing special about using OFFSET in an INVOKE. Don't be superstitious.

However, in this case you gotta use ADDR:
DoSomething    PROC
      LOCAL string[32]:BYTE

      INVOKE  SendMessage, hWin, WM_SETTEXT, 0, ADDR string
Title: Re: Win32 My First Window
Post by: sinsi on May 03, 2024, 11:17:52 AM
.data?
hinst DWORD ?
.code
tester PROC
LOCAL LocalVar:DWORD
    mov eax,offset hinst          ;ok
    mov eax,addr [hinst]          ;syntax error
    mov eax,addr hinst            ;syntax error
    invoke MyProc,addr hinst      ;ok, hinst is in .data, ML emits "OFFSET hinst"
    invoke MyProc,offset hinst    ;ok
    invoke MyProc,addr LocalVar   ;ok, ML emits "LEA EAX,LocalVar"  "PUSH EAX"
    invoke MyProc,offset LocalVar ;error A2098:invalid operand for OFFSET
Title: Re: Win32 My First Window
Post by: NoCforMe on May 03, 2024, 11:18:38 AM
More "refinement". Looking at Sudoku's converted code:
    WndProc proc hWnd:HWND, message:UINT, wParam:WPARAM, lParam:LPARAM
    local hdc:HDC
    local rect:RECT
   
    SWITCH message
    CASE WM_PAINT
        invoke BeginPaint, hWnd, offset ps    ; was addr ps. ps is a global so offset must be used
        mov hdc, eax
While that'll work, it's a bit strange to have some of the paint variables in the routine as LOCALs and some as globals. I'd make them all LOCALs, in which case you'd need to use ADDR instead of OFFSET:
    local ps:PAINTSTRUCT

    invoke BeginPaint, hWnd, ADDR ps
Those variables kind of go together as a "set". Plus you never have to worry about any collisions between variables if you have more than one WM_PAINT handler (I have programs that have several).
Plus the vars don't take up any space in the executable.
Title: Re: Win32 My First Window
Post by: sudoku on May 03, 2024, 11:24:54 AM
Quote from: NoCforMe on May 03, 2024, 11:18:38 AMMore "refinement". Looking at Sudoku's converted code:
Link?
Title: Re: Win32 My First Window
Post by: NoCforMe on May 03, 2024, 11:27:47 AM
It was in reply #102 (https://masm32.com/board/index.php?msg=130068), and it sure looks like your (Sudoku's) code, although it was attached by the OP. ???
Title: Re: Win32 My First Window
Post by: sudoku on May 03, 2024, 11:53:29 AM
Quote from: NoCforMe on May 03, 2024, 11:27:47 AMIt was in reply #102 (https://masm32.com/board/index.php?msg=130068), and it sure looks like your (Sudoku's) code, although it was attached by the OP. ???
Surely you jest...
I won't even dignify that with a more suitable response. 

A more suitable response posted below in post #114.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 03, 2024, 12:07:15 PM
So that's not your code? If so, sorry. No need to get on your high horse. (I said it looked like your code.)
Title: Re: Win32 My First Window
Post by: sudoku on May 03, 2024, 12:11:11 PM
Quote from: NoCforMe on May 03, 2024, 12:07:15 PMSo that's not your code? If so, sorry. No need to get on your high horse. (I said it looked like your code.)

quite certain, 100% in fact. further reading in case you missed it...

Quote from: tda0626 on May 03, 2024, 05:34:23 AM
Quote from: TimoVJL on May 02, 2024, 06:41:12 PMMany of us remember this, from Petzold book:
HelloWin petzold-pw5e (https://github.com/recombinant/petzold-pw5e/blob/master/Chapter%2003%20Windows%20and%20Messages/01%20HelloWin/hellowin.c)

Yep, I have a copy of this book and translated his code over to assembler for practice.

Tim
Title: Re: Win32 My First Window
Post by: TimoVJL on May 03, 2024, 04:59:01 PM
Quote from: NoCforMe on May 03, 2024, 11:18:38 AMThose variables kind of go together as a "set". Plus you never have to worry about any collisions between variables if you have more than one WM_PAINT handler (I have programs that have several).
Plus the vars don't take up any space in the executable.

Neither .data? section take any space in the executable and are zeroes at beginning.
But globals aren't so good for portable functions.

EDIT:
Strings are global and optimizers use same similar strings to avoid duplicates.
This thread gives some info for converting C code to asm in many ways.
Title: Re: Win32 My First Window
Post by: jj2007 on May 03, 2024, 05:28:45 PM
Quote from: sudoku on May 03, 2024, 10:58:24 AMI never use offset in an invoke statement. And I have rarely seen any other source code with it

You haven't seen my sources :biggrin:

Surely, it's a matter of taste, and offset is two characters more to type, but with more complex sources you will quickly appreciate to know at one glance "oh, it's a global variable".
Title: Re: Win32 My First Window
Post by: sinsi on May 03, 2024, 05:31:56 PM
Quote from: jj2007 on May 03, 2024, 05:28:45 PMbut with more complex sources you will quickly appreciate to know at one glance "oh, it's a global variable".
Yep

Quote from: TimoVJL on May 03, 2024, 04:59:01 PMBut globals aren't so good for portable functions.
Or multithreaded functions.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 04, 2024, 04:12:29 AM
Quote from: tda0626 on May 03, 2024, 10:03:08 AM
Quote from: NoCforMe on May 03, 2024, 06:58:51 AM
Quote from: tda0626 on May 03, 2024, 05:48:47 AMLooked at his text metrics code and besides the custom include file he wants you to import into the program, I have no clue to translating some of his c code in that section.
What program is that exactly? I have his book (yes, actual book, the size of a boat anchor) in front of me. I could take a look at that code.
In the 5th edition, it is on page 73. The custom include file starts on page 70.
I just checked my book (yes, the actual physical book, which makes a pretty good doorstop), and there's no program on either of those pages. Could you tell me which program exactly it is you want to convert? Petzold names all his programs ("SYSMETS1", etc.). I'll be happy to take a look at it. I won't "do your homework" but I may be able to offer some pointers (pun intended).
Title: Re: Win32 My First Window
Post by: sudoku on May 04, 2024, 05:02:13 AM
Quote from: NoCforMe on May 04, 2024, 04:12:29 AMI'll be happy to take a look at it.
I am fairly certain (75%) it is the code where TimoVJL's link points to. ( if you are refering to his 'converted' code) Thats why I posted that link above. #114

Quote from: TimoVJL on May 02, 2024, 06:41:12 PMMany of us remember this, from Petzold book:
HelloWin petzold-pw5e (https://github.com/recombinant/petzold-pw5e/blob/master/Chapter%2003%20Windows%20and%20Messages/01%20HelloWin/hellowin.c)
Click the link and see...

Else there is a header file "SysMets.h" a bit further down that file tree there. (the "custom include file")???
 header file  (https://github.com/recombinant/petzold-pw5e/blob/master/Chapter%2004%20An%20Exercise%20in%20Text%20Output/01%20SysMets1/SysMets.h)
That sounds to me like what he means by "Looked at his text metrics code and besides the custom include file he wants you to import into the program"
Title: Re: Win32 My First Window
Post by: NoCforMe on May 04, 2024, 05:53:37 AM
I'll let the OP answer this question, thanks.
Title: Re: Win32 My First Window
Post by: tda0626 on May 04, 2024, 05:59:24 AM
Quote from: sudoku on May 04, 2024, 05:02:13 AM
Quote from: NoCforMe on May 04, 2024, 04:12:29 AMI'll be happy to take a look at it.
I am fairly certain (75%) it is the code where TimoVJL's link points to. ( if you are refering to his 'converted' code) Thats why I posted that link above. #114

Quote from: TimoVJL on May 02, 2024, 06:41:12 PMMany of us remember this, from Petzold book:
HelloWin petzold-pw5e (https://github.com/recombinant/petzold-pw5e/blob/master/Chapter%2003%20Windows%20and%20Messages/01%20HelloWin/hellowin.c)
Click the link and see...

Else there is a header file "SysMets.h" a bit further down that file tree there. (the "custom include file")???
 header file  (https://github.com/recombinant/petzold-pw5e/blob/master/Chapter%2004%20An%20Exercise%20in%20Text%20Output/01%20SysMets1/SysMets.h)
That sounds to me like what he means by "Looked at his text metrics code and besides the custom include file he wants you to import into the program"

How would you do that include file to masm syntax?
Title: Re: Win32 My First Window
Post by: sudoku on May 04, 2024, 05:59:36 AM
Quote from: NoCforMe on May 04, 2024, 05:53:37 AMI'll let the OP answer this question, thanks.
Well, you did invoke my name regarding his code...
Quote from: NoCforMe on May 03, 2024, 11:18:38 AMMore "refinement". Looking at Sudoku's converted code:
:smiley:  Just trying to lend a hand. And I was curious, so I looked through Timos link for myself... and thought that I would share what I found.
Title: Re: Win32 My First Window
Post by: jj2007 on May 04, 2024, 06:18:18 AM
Quote from: tda0626 on May 04, 2024, 05:59:24 AMHow would you do that include file to masm syntax?

Check this thread. (https://masm32.com/board/index.php?topic=9486.msg103764#msg103764)
Title: Re: Win32 My First Window
Post by: NoCforMe on May 04, 2024, 06:29:36 AM
Quote from: tda0626 on May 04, 2024, 05:59:24 AMElse there is a header file "SysMets.h" a bit further down that file tree there. (the "custom include file")???
 header file  (https://github.com/recombinant/petzold-pw5e/blob/master/Chapter%2004%20An%20Exercise%20in%20Text%20Output/01%20SysMets1/SysMets.h)
How would you do that include file to masm syntax?
If you're referring to SYSMETS.H, easy peasy. I'll actually do part of your homework here for you, since you'll end up having to do a shitload of typing anyhow.

All this file consists of is 1) a size equate (NUMLINES), 2) a structure definition (sysmetrics) and 3) a whole bunch of statements that fill in that structure.

First the equate:
#define NUMLINES ((int)(sizeof sysmetrics) / sizeof sysmetrics[0]))
This simply gives you the number of lines, which is the size of the entire structure divided by the size of one element. We'll deal with this in a bit, but first the structure definition:
struct
{
    int      iIndex;
    TCHAR *  szLabel;
    TCHAR *  szDesc;
}
easily translates to
SYSMETRIC_ENTRY    STRUCT
  iIndex      DD ?
  labelText   DD ?
  descText    DD ?
SYSMETRIC_ENTRY    ENDS
(I've given the structure itself a name, which wasn't needed in the original C program)

Now that you have the structure, defining the elements is easy. Except for one thing: in C, you can define the text that goes into the last 2 members AND put a pointer to them into the structure with the TEXT (C) macro.
As is well-known around here, I don't like using macros in assembly language, so the way I would do this is to first define all the strings, give them names, then put those names into the structure. This might seem a pain in the ass to you, so I'm going to be merciful and use the asm macro chr$ so you can put the text directly into the structure as in Petzold. Here are the first 2 elements of that structure:
SYSMETRIC_ENTRY <SM_CXSCREEN, chr$("SM_CXSCREEN"), chr$("Screen width in pixels")>
SYSMETRIC_ENTRY <SM_CYSCREEN, chr$("SM_CYSCREEN"), chr$("Screen height in pixels")>
That takes care of that structure; you just have to type in the rest of the entries.
BTW, @JJ: I hope I've used this macro correctly here. You can correct me if I've misused it.

Now to the matter of determining the number of "lines" (# of SYSMETRIC_ENTRY items):
While we can take the SIZEOF the structure in assembly language, there's no equivalent to the C operator "sizeof sysmetrics" which gives the size of the entire array of structures.

However, that's not difficult to do. What I would do is this:
sysmetrics    LABEL SYSMETRIC_ENTRY
SYSMETRIC_ENTRY <SM_CXSCREEN, chr$("SM_CXSCREEN"), chr$("Screen width in pixels")>
SYSMETRIC_ENTRY <SM_CYSCREEN, chr$("SM_CYSCREEN"), chr$("Screen height in pixels")>

    . . .
    . . .

$sysmetricsSize    EQU $ - sysmetrics    ;This is the size of the array of structs, in bytes.
and then make the size equate thus:
NUMLINES    EQU $sysmetricsSize / SIZEOF SYSMETRIC_ENTRY
BTW, another way to skin that cat, and the way that I prefer to do things, is to put a signal value at the end of the list, rather than calculating the number of items in the list and looping that number of times when displaying the list. Forget NUMLINES, and create the list thus:
SYSMETRIC_ENTRY <SM_CXSCREEN, chr$("SM_CXSCREEN"), chr$("Screen width in pixels")>
SYSMETRIC_ENTRY <SM_CYSCREEN, chr$("SM_CYSCREEN"), chr$("Screen height in pixels")>

    . . .
    . . .

      DD -1  ;STOP when iIndex = -1.
You'll have to modify his program to check for a -1 value in the first member of the structure (iIndex) to know when you've hit the end of the list. I'll leave it to you as an exercise if you want to go this way instead.

That's it.

Concerning the mechanics of actually converting this header file to assembly language, I realize it's not quite "easy peasy" because of all the typing and reformatting required. What you might want to do is to take the original .h file and reformat it in your editor; that way you have all the strings and constants you need. You just need to change this
    SM_CXSCREEN,          TEXT ("SM_CXSCREEN"),
                          TEXT ("Screen width in pixels"),
to this
SYSMETRIC_ENTRY <SM_CXSCREEN, chr$("SM_CXSCREEN"), chr$("Screen width in pixels")>
which isn't pleasant but not an impossible task either. Mostly a lot of copying and pasting.
Title: Re: Win32 My First Window
Post by: tda0626 on May 04, 2024, 08:44:35 AM
I modified it in Excel so check and see if it looks right. The inc file is attached.


Tim
Title: Re: Win32 My First Window
Post by: NoCforMe on May 04, 2024, 08:56:26 AM
Wow. I'm impressed.
Never thought of using Excel; I use it occasionally, but am not an expert. Does it have more extensive search-and-replace capabilities than an editor?

Anyhow, looks OK. Let us know if it assembles correctly.
Title: Re: Win32 My First Window
Post by: jj2007 on May 04, 2024, 09:04:12 AM
Quote from: tda0626 on May 04, 2024, 08:44:35 AMI modified it in Excel

Three columns - clever :thumbsup:

Quote from: NoCforMe on May 04, 2024, 08:56:26 AMDoes it have more extensive search-and-replace capabilities than an editor?

Better than most editors ;-)
Title: Re: Win32 My First Window
Post by: sudoku on May 04, 2024, 09:06:55 AM
Looks good :thumbsup:
I would have cooked up a qeditor plugin to handle the task.  :azn:
Title: Re: Win32 My First Window
Post by: tda0626 on May 04, 2024, 09:20:23 AM
Quote from: NoCforMe on May 04, 2024, 08:56:26 AMWow. I'm impressed.
Never thought of using Excel; I use it occasionally, but am not an expert. Does it have more extensive search-and-replace capabilities than an editor?

Anyhow, looks OK. Let us know if it assembles correctly.

Thanks!

When I imported the copy from the book to Excel, I used commas as a separator but it didn't quite work the way I wanted so I had to do some coping and pasting to line everything up and used the find and replace feature to get rid of unwanted words or characters in the cells and to get down to a base line look. Then I used the =trim formula to get rid of unwanted spaces. After that, I used =concat formula to add the extra characters before and after the strings.
Title: Re: Win32 My First Window
Post by: tda0626 on May 04, 2024, 10:30:51 AM
I am converting Charles Petzold's code over to assembly and I came across this

cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;

So what I think it does is this in my own way

.if tm.tmPitchAndFamily and 1
       
            shr cxChar, 1
            mov eax, cxChar
            test eax
            jz ODD
            add eax, cxChar
            mov cxCaps, eax
            ODD:
            add eax, cxChar
            sub eax,1
            mov cxCaps, eax
           
        .else
       
            mov eax, cxChar
            mov cxCaps, eax
           
        .endif

Is that close or somewhat right?


Tim
Title: Re: Win32 My First Window
Post by: NoCforMe on May 04, 2024, 10:41:36 AM
Um, no, you've kinda got the cart before the horse there.
Let's parse that statement:
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
That says:
"cxCaps =
  (if (tm.tmPitchAndFamily AND 1) is non-zero--> 3
  else                                           2)
* cxChar / 2
You need to do the conditional test on tm.tmPitchAndFamily first (to get either 2 or 3), then multiply by cxChar / 2.

Hint: (something) / 2 = SHR  (something), 1.
Title: Re: Win32 My First Window
Post by: tda0626 on May 04, 2024, 10:56:25 AM
So if it is 2, then the statement is saying essentially multiply cxChar by 1 because shr 1 and then shl 1, so just need to multiply by 3 right?



.if tm.tmPitchAndFamily and 1 == 0

mov eax, cxChar
mov cxCaps, eax

.else

shr cxChar, 1
add eax, cxChar
sub eax,1
mov cxCaps, eax

Title: Re: Win32 My First Window
Post by: tda0626 on May 04, 2024, 11:02:16 AM
This has me hung up too.


TextOut (hdc, 0, cyChar * i, sysmetrics[i].szLabel,lstrlen (sysmetrics[i].szLabel)) ;

Not sure how to reference my structure like he is doing

sysmetrics[i].szLabel,

This is as far as I have gotten with it

invoke TextOutA, hdc, 0, cyChar * i,
Title: Re: Win32 My First Window
Post by: NoCforMe on May 04, 2024, 12:42:45 PM
Well, you're not writing C here, so you can't use expressions like cyChar * i in an INVOKE statement. You'll need to do your address calculations before the call to TextOut().

Don't have the program code in front of me, so don't know where your index, i, is coming from, but let's assume it's in a variable called "index":
    LOCAL  y:DWORD

    MOV    EAX, index
    MUL    cyChar          ;i * cyChar
    MOV    y, EAX          ;Save the result (the y-position in the display)

; call some flavor of strlen() here, save its result in ECX

    MOV    EAX, SIZEOF SYSMETRIC_ENTRY
    MUL    index         
    ADD    EAX, OFFSET sysmetrics  ;EAX points to SYSMETRIC_ENTRY[index]
    MOV    EAX, [EAX].SYSMETRIC_ENTRY.labelText  ;EAX--> text to display

    INVOKE  TextOut, hdc, 0, y, EAX, ECX
I don't know what flavor of strlen() you'll be using (I have my own homegrown version), so you'll have to figure that part out. You'll probably have to put it after the address calculation and somehow preserve the results of the previous calculation (we're running out of registers here, so you might need a couple more local variables).

Note: keep in mind that MUL overwrites EDX (EAX x [something]--> EDX:EAX).

Hopefully you get the idea.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 04, 2024, 02:29:27 PM
Here's the complete conversion, using the StrLen() function from the MASM32 library:

LOCAL y:DWORD, stringPtr:DWORD
; Assuming that "i" is in a variable called "index":

; Calculate y-position:
MOV EAX, index
MUL cyChar
MOV y, EAX ;Save in local var.

; Get address of string to display:
MOV EAX, SIZEOF SYSMETRIC_ENTRY
MUL index
ADD EAX, OFFSET sysmetrics
MOV stringPtr, EAX

; Get length of string (using StrLen() from MASM32 library):
INVOKE StrLen, EAX

; Display the string:
INVOKE TextOut, hdc, 0, y, stringPtr, EAX
Title: Re: Win32 My First Window
Post by: tda0626 on May 04, 2024, 10:54:52 PM
Worked out almost all of the compile errors but this one

sysmets1.asm(195) : error A2026: constant expected

195                          mov eax, NUMLINES

Title: Re: Win32 My First Window
Post by: jj2007 on May 04, 2024, 11:18:18 PM
Have you defined NUMLINES somewhere?

NUMLINES=123 or
NUMLINES equ <123>
Title: Re: Win32 My First Window
Post by: tda0626 on May 04, 2024, 11:22:28 PM
Quote from: jj2007 on May 04, 2024, 11:18:18 PMHave you defined NUMLINES somewhere?

NUMLINES=123 or
NUMLINES equ <123>


It was defined in that inc file I made.

Definition is
NUMLINES EQU  $sysmetricsSize / sizeof SYSMETRIC_ENTRY

SYSMETRIC_ENTRY STRUCT

iIndex    DD ?
szLabel   DD ?
szDesc    DD ?

SYSMETRIC_ENTRY    ENDS

sysmetrics    LABEL SYSMETRIC_ENTRY

SYSMETRIC_ENTRY <SM_CXSCREEN, chr$("SM_CXSCREEN"), chr$("Screen width in pixels")>
...

$sysmetricsSize    EQU $ - sysmetrics
Title: Re: Win32 My First Window
Post by: jj2007 on May 05, 2024, 02:27:04 AM
Try NUMLINES = $sysmetricsSize / sizeof SYSMETRIC_ENTRY after $sysmetricsSize EQU $ - sysmetrics
Title: Re: Win32 My First Window
Post by: tda0626 on May 05, 2024, 02:35:00 AM
I got the same error message. I have been trying to figure this out for hours now.
Title: Re: Win32 My First Window
Post by: jj2007 on May 05, 2024, 02:57:41 AM
Post complete code, and I'll see.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 04:40:34 AM
Got to be something simple.
What assembler are you using?
Title: Re: Win32 My First Window
Post by: tda0626 on May 05, 2024, 05:00:54 AM
Quote from: NoCforMe on May 05, 2024, 04:40:34 AMGot to be something simple.
What assembler are you using?

MASM

Quote from: jj2007 on May 05, 2024, 02:57:41 AMPost complete code, and I'll see.

It is attached with the inc file.

Please take a look at the WM_PAINT section. I had a hard time translating the c code over even with NoCforMe's help. I am sure there are some errors the compiler isn't picking up. The Petzold code is below just in case someone doesn't have this book.

case WM_PAINT :
 hdc = BeginPaint (hwnd, &ps) ;
 for (i = 0 ; i < NUMLINES ; i++)
 {
 TextOut (hdc, 0, cyChar * i,
 sysmetrics[i].szLabel,
 lstrlen (sysmetrics[i].szLabel)) ;
 TextOut (hdc, 22 * cxCaps, cyChar * i,
 sysmetrics[i].szDesc,
 lstrlen (sysmetrics[i].szDesc)) ;
 SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;
 TextOut (hdc, 22 * cxCaps + 40 * cxChar, cyChar * i, szBuffer,
 wsprintf (szBuffer, TEXT ("%5d"),
 GetSystemMetrics (sysmetrics[i].iIndex))) ;
 SetTextAlign (hdc, TA_LEFT | TA_TOP) ;
 }
 EndPaint (hwnd, &ps) ;
 return 0 ;
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 05:34:16 AM
I made your sysmets.inc into a stand-alone .asm file (added what was needed to make it assemble). It assembled without errors. All I added was include \masm32\include\masm32rt.inc (needed for the chr$ macro).

I did have a really weird problem after I downloaded the file. It originally opened with Notepad, and I edited it there. For some reason there were 3 characters at the beginning of the file, non-ASCII, that I could not get rid of and which caused assembler errors ("invalid character in file"). Only after I edited it with my own editor (EdAsm) did those spurious characters disappear. ??? But I suspect this may have nothing to do with your problem.

Hmm; after looking at your .asm source (sysmets1.asm), maybe the problem is that you're including your .inc file immediately after the MASM32 include file:
.386

option    casemap: none

include \masm32\include\masm32rt.inc
include sysmets.inc
which may be bad because you open a .data section immediately in the .inc file.
Try putting that include after your .data statement (and remove .data from the .inc file). After all, all it's really doing is defining data (plus a couple constants based on that data).
Also, there's no need for the .386 and the option casemap statements, since they're covered in masm32rt.inc.
Also, and this is just icing on the cake, I'd bracket the include for masm32rt.inc with .nolist and .list. Otherwise your listing (.lst) file will be needlessly humongous. (I don't know why Hutch didn't put that in that file.)

Title: Re: Win32 My First Window
Post by: sudoku on May 05, 2024, 05:35:03 AM
"NUMLINES EQU  $sysmetricsSize / sizeof SYSMETRIC_ENTRY" in your include file...

Masm detects a "/" in the file as a line continuation character...? whoops... line continuation is "\"
I rarely if ever, use that. Hence my mistake. :tongue:
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 05:37:47 AM
Nope. That line assembled fine for me; "/" is the division operator in MASM.
The backslash ("\") is the line continuation character.
Title: Re: Win32 My First Window
Post by: sudoku on May 05, 2024, 05:43:35 AM
Quote from: NoCforMe on May 05, 2024, 05:37:47 AMThe backslash ("\") is the line continuation character.
Yes, I checked and corrected my post. My bad.  :cool:
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 06:19:08 AM
Aaaaaargh. See, this is why I hate macros.

JJ, since you're an expert in this area, could you take a look at the attached listing file? This is the result of assembling his sysmets1.asm.

What a frigging mess! That chr$ macro bounces back and forth between .data and .code. (Why?) And I honestly cannot tell from that listing what is going on in terms of what data is actually being defined there. I think that may have something to do with this problem. I get the same error, "constant expected" with NUMLINES. I don't think the length of that data area (sysmetrics) can be determined correctly for some reason.

Of course I understand that without using the chr$ macro it would be a total pain in the ass to create this structure, as you'd have to create each and every string individually, give it a name, then plug those names into the structure.

Help!

Just to be crystal clear, what is needed is for chr$ to create a separate anonymous string in .data and put a pointer to that string in the SYSMETRIC_ENTRY structure (not the string data itself).
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 06:40:08 AM
Sorry, my bad: I see the problem. chr$ is the wrong macro.
It's used when you want to include a text string in code, not data: it creates an anonymized (nameless) string in .data but emits the address of that string in .code so it can be used in, say, an INVOKE. So it ain't gonna work here.

What's needed is the equivalent to the C TEXT macro, that (somehow) creates the anonymized string in the data section but in a different place from the current location, then puts a pointer to that text where you need it. How would it even do that?

I've enlisted the help of JJ, who's a macro expert. Hopefully he can help us sort this out.
Title: Re: Win32 My First Window
Post by: jj2007 on May 05, 2024, 07:00:37 AM
Quote from: NoCforMe on May 05, 2024, 06:40:08 AMWhat's needed is the equivalent to the C TEXT macro, that (somehow) creates the anonymized string in the data section

From A window in 30 lines of code (https://masm32.com/board/index.php?topic=11453.0):

.data   ; initialised data section
wc   WNDCLASSEX <WNDCLASSEX, 0, WndProc, 0, 0, 1, 2, 3, COLOR_BTNFACE+1, 0, txClass, 4>
txClass   db "Masm32GUI", 0         
Title: Re: Win32 My First Window
Post by: TimoVJL on May 05, 2024, 07:57:04 AM
Quote from: NoCforMe on May 05, 2024, 06:40:08 AMWhat's needed is the equivalent to the C TEXT macro, that (somehow) creates the anonymized string in the data section but in a different place from the current location, then puts a pointer to that text where you need it. How would it even do that?

C TEXT() macro is only for selecting between ANSI or UNICODE

TEXT macro (winnt.h) (https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-text)

#ifdef  UNICODE
#define __TEXT(quote) L##quote
#else
#define __TEXT(quote) quote
#endif
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 07:58:12 AM
But but but ... hmmm, that's not a macro; that's the "pain-in-the-ass" way I said would be the other way to do this.

Isn't there some macro that behaves like C TEXT? (Although I have no idea how that would work: it would have to define the anonymous strings in some other part of the data section than what was being defined.)

Something like:
S <SM_CXSCREEN, TEXT("SM_CXSCREEN"), TEXT("Screen width in pixels")>
such that data would end up being defined thus:
S <SM_CXSCREEN, ?000001, ?000002>

 . . . . .

?000001  DB "SM_CXSCREEN", 0
?000002  DB "Screen width in pixels", 0
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 08:01:58 AM
Quote from: TimoVJL on May 05, 2024, 07:57:04 AMC TEXT() macro is only for selecting between ANSI or UNICODE

TEXT macro (winnt.h) (https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-text)

#ifdef  UNICODE
#define __TEXT(quote) L##quote
#else
#define __TEXT(quote) quote
#endif
Timo, that's __TEXT, not TEXT. Petzold uses it all over the place in his book:
struct {
    int
    TCHAR * szLabel;
    TCHAR * szDesc;
}
sysmetrics[] =
{  SM_CSSCREEN, TEXT ("CM_CXSCREEN"), TEXT ("Screen width in pixels"),
    . . .
Anyhow, whatever it's called, it's exactly what we need here, but in MASM.
Title: Re: Win32 My First Window
Post by: TimoVJL on May 05, 2024, 08:04:21 AM
I just missed to add this too
#define TEXT(quote) __TEXT(quote)
poasm accept thisstr$ MACRO any_text:VARARG
LOCAL txtname
txtname db any_text,0
EXITM <OFFSET txtname>
ENDM
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 08:15:13 AM
Quote from: tda0626 on May 05, 2024, 05:00:54 AM----

OK, in the meantime while we're trying to see if there exists a suitable macro, you can get this working by coding it up thus:
First, define all the strings and give them (simple) names (for the sake of avoiding too much typing):
S1a  DB "SM_CXSCREEN", 0
S1b  DB "Screen width in pixels", 0
and so on.

Then just stick those names into the structure:
SYSMETRIC_ENTRY <SM_CXSCREEN, S1a, S1b>
and so on. It's a bit of typing, but maybe you can semi-automate it with Excel or something. You seem pretty resourceful.

It'll work, I promise.
Title: Re: Win32 My First Window
Post by: sudoku on May 05, 2024, 08:19:17 AM
Hmmm.. maybe find someone who can compile Petzolds C example, and attach the executable... then look at the resultant disassembly??? Should give some workable ideas how to approach the problem in assembly.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 08:24:55 AM
I don't think that would help.
I'm willing to bet that it would show the structure and the strings that are pointed to in that structure end up in two different parts of .data. What it wouldn't show is how those strings got put in that separate place (i.e., how the TEXT macro functions).

Keep in mind that the structure has to be contiguous, with no intervening data (like text) between or in any of the elements.
Title: Re: Win32 My First Window
Post by: sudoku on May 05, 2024, 08:30:46 AM
How about this TEXT macro (https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-text)
It has to be defined somewhere. Meaning the contents of the macro.

I just now noticed that TimoVJL poted the same link and info already... above.

Will either of these work? Seems to be what the TEXT macro is doing as far as I can tell.
    STRING MACRO sname,quoted_text
      LOCAL quoted@@@@
      .data
        quoted@@@@ db quoted_text,0
        align 8
        sname dq quoted@@@@
      .code
    ENDM

      STRING MACRO data_label,quoted_text:VARARG
        IFNDEF __UNICODE__
          .data
            data_label db quoted_text,0
            align 4
          .code
        ELSE
          WSTR data_label,quoted_text,0
        ENDIF
      ENDM
The first is from the masm64 sdk, the second from masm32 sdk.
Title: Re: Win32 My First Window
Post by: sudoku on May 05, 2024, 08:50:03 AM
...
Dangit! I quoted my post instead of modifying it.

Mods!! delete this errant post, please.  :smiley:
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 08:55:58 AM
NO!
Can't you see that those macros would declare the text string at the place where the macro is being invoked? Meaning right smack in the middle of the structure, which won't work. We want pointers to text strings in the structure, not the strings themselves.

Besides, both of those macros shift back to .code, which is definitely not wanted here! It's all got to be in .data.
Title: Re: Win32 My First Window
Post by: sinsi on May 05, 2024, 09:01:20 AM
cstr MACRO lbl,string:VARARG
LOCAL s,x,z
    IFDEF __UNICODE__
        align 2
        IFNB <lbl>
            lbl LABEL WORD
        ENDIF
        FOR s,<string>
            IFIDN @SubStr(s,1,1),<">
                x SUBSTR <s>,2,(@SizeStr(s)-2)
                % FORC c,<x>
                     dw "&c"
                  ENDM
            ELSE
                dw s
            ENDIF
        ENDM
        dw 0
    ELSE
        IFNB <lbl>
            lbl LABEL BYTE
        ENDIF
        FOR s,<string>
            IFIDN @SubStr(s,1,1),<">
                x SUBSTR <s>,2,(@SizeStr(s)-2)
                % FORC c,<x>
                     db "&c"
                  ENDM
            ELSE
                db s
            ENDIF
        ENDM
        db 0
    ENDIF
ENDM

@cstr MACRO string:VARARG
LOCAL s,sss1
sss1 TEXTEQU @CurSeg
.const
cstr s,string
IFIDNI sss1,<_TEXT>
.code
ELSEIFIDNI sss1,<_BSS>
.data?
ELSEIFIDNI sss1,<_DATA>
.data
ENDIF
EXITM <offset s>
ENDM
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 09:12:09 AM
OK. Did you just come up with that? If so, appreciate the effort.
However, I have no friggin' idea what those do or how they work.
Care to explain?
Where (in the .data section) do the strings get declared?

Could you show the OP how to use this?

One possible clue: @cstr includes .const; is this a separate section of initialized data where the strings go?
And then the macro emits offset s before it exits, giving us the pointer; is that right?
Title: Re: Win32 My First Window
Post by: sudoku on May 05, 2024, 09:18:11 AM
Quote from: NoCforMe on May 05, 2024, 08:55:58 AMWe want pointers to text strings in the structure, not the strings themselves.
I played around a bit with those. I did get a pointer into the code section, but not into a structure. Nothing ventured, nothing gained. I am obviously NOT a macro guy.  :tongue:


@sinsi: but will that work within a structure? Does it need parenthesis? example cstr(mystring, "my string")
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 09:18:59 AM
You and me both, pal.
Title: Re: Win32 My First Window
Post by: sinsi on May 05, 2024, 11:52:30 AM
test1 struct
 appname dd ?
test1 ends

.data
test2 test1 <@cstr("Hi")>

.code
start:
    mov eax,test2.appname ;  = pointer to db "Hi",0
Title: Re: Win32 My First Window
Post by: sudoku on May 05, 2024, 11:56:37 AM
@sinsi:  :thumbsup:
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 12:09:12 PM
OK, man of few words:
Does the OP have to type all that stuff in?
Is it already in the MASM32 stuff?
Title: Re: Win32 My First Window
Post by: sinsi on May 05, 2024, 12:17:46 PM
OK, man of few words:
 :biggrin:

Does the OP have to type all that stuff in?
  copy paste should work too

Is it already in the MASM32 stuff?
  No, it's mine, all mine mwuhaha

it's been a long night...
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 12:22:45 PM
Tested?
Title: Re: Win32 My First Window
Post by: sinsi on May 05, 2024, 12:27:05 PM
Quote from: NoCforMe on May 05, 2024, 12:22:45 PMTested?
yep (https://masm32.com/board/index.php?msg=130184)
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 12:37:49 PM
OK, OP: since you've already done all that reformatting, my suggestion is to use sinsi's macros. Just copy and paste his code into your main program, somewhere near the top. (Or put it in an include file, but probably easier for now to just plunk it into your code.)

Then just replace all those chr$s with @cstr.

According to him, it should work.

We await your results.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 12:39:41 PM
Quote from: sinsi on May 05, 2024, 12:27:05 PM
Quote from: NoCforMe on May 05, 2024, 12:22:45 PMTested?
yep (https://masm32.com/board/index.php?msg=130184)
I would like to know, however, how those macros work.

Is .const a different initialized data area, separate from .data?
Pardon my ignorance; never heard of that nor used it.
Title: Re: Win32 My First Window
Post by: tda0626 on May 05, 2024, 12:46:35 PM
Quote from: NoCforMe on May 05, 2024, 12:37:49 PMOK, OP: since you've already done all that reformatting, my suggestion is to use sinsi's macros. Just copy and paste his code into your main program, somewhere near the top. (Or put it in an include file, but probably easier for now to just plunk it into your code.)

Then just replace all those chr$s with @cstr.

According to him, it should work.

We await your results.

OK so I can leave the original inc intact except for that modification? Sorry so many macros thrown around in the last couple of threads my eyeballs are going cross eyed. :joking: That stuff is like a foreign language to me.


Tim

Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 12:53:56 PM
Yes; just copy the macros into it without understanding what they do. (Maybe later ... much later.)

It's pretty much Greek to me, too.
Title: Re: Win32 My First Window
Post by: sinsi on May 05, 2024, 01:47:02 PM
OK, some comments
;Define a string in the current segment, optionally give it a name
;usage example
;.data
;    cstr MyString,"G'day",13,10
cstr MACRO lbl,string:VARARG
LOCAL s,x,z
    ;;Unicode string
    IFDEF __UNICODE__
        ;;align to unicode's size (16 bits)
        align 2
;;IF lbl is Not Blank
        IFNB <lbl>
    ;;define the label
            lbl LABEL WORD
        ENDIF
;;FOR each element s in string
        FOR s,<string>
    ;;if the first char is quotes
            IFIDN @SubStr(s,1,1),<">
        ;;strip the quotes
                x SUBSTR <s>,2,(@SizeStr(s)-2)
;;FOR each Char in the unquoted string
                % FORC c,<x>
    ;;simplified ASCII to unicode
                    dw "&c"
                ENDM
            ;;no quotes, must be a simple byte (like 13 (CR) or 10 (LF))
            ELSE
                dw s
            ENDIF
        ENDM
;;terminating zero char
        dw 0
    ;ASCII string - steps are the same except 1 byte = 1 char
    ELSE
        IFNB <lbl>
            lbl LABEL BYTE
        ENDIF
        FOR s,<string>
            IFIDN @SubStr(s,1,1),<">
                x SUBSTR <s>,2,(@SizeStr(s)-2)
                % FORC c,<x>
                     db "&c"
                  ENDM
            ELSE
                db s
            ENDIF
        ENDM
        db 0
    ENDIF
ENDM

;Define a string in the .const segment and return its offset
;Use it with INVOKE or in a structure, wherever you need the offset of a string
;Segment-aware, so it will restore the current segment after defining the string in .const
@cstr MACRO string:VARARG
LOCAL s,sss1
;;save the current segment
sss1 TEXTEQU @CurSeg
;;define the string in .const
;;Warning: the .const section is READ-ONLY, you won't be able to alter the string
.const
cstr s,string
;;Restore the segment in use, if I missed one just add it here :)
IFIDNI sss1,<_TEXT>
.code
ELSEIFIDNI sss1,<_BSS>
.data?
ELSEIFIDNI sss1,<_DATA>
.data
ENDIF
;;return the offset of the string
;;32-bit code only
EXITM <offset s>
ENDM
There are plenty of opportunities to streamline it, but it's good enough for me (and I HATE macros :biggrin: )
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 02:17:53 PM
Thanks--that helps.
So we're basically declaring the strings in one segment (.const) and putting their offsets in another segment, where our structure lives.

Cool. That's all I need to know; don't want all the gory details on how the sausage is made.

Pretty good for someone who hates macros!
Title: Re: Win32 My First Window
Post by: jj2007 on May 05, 2024, 05:41:52 PM
Quote from: NoCforMe on May 05, 2024, 02:17:53 PMSo we're basically declaring the strings in one segment (.const) and putting their offsets in another segment, where our structure lives.

More or less (it can be in the same segment, too)

include \masm32\include\masm32rt.inc
include cStr.mac

test1 struct
 appname dd ?
test1 ends

.data
test2 test1 <@cStr("Hi")>
test3 test1 <thestring>

.code
thestring db "why do you need a macro for doing that?", 0    ; could be in .data, too

start:
    mov eax, test2.appname ;  = pointer to db "Hi",0
    print eax, " there, test2", 13, 10

    mov eax, test3.appname
    inkey eax
  exit

end start
Title: Re: Win32 My First Window
Post by: TimoVJL on May 05, 2024, 06:48:37 PM
testing with poasm:
.386
.model flat, stdcall

printf PROTO C :PTR,:VARARG
INCLUDELIB msvcrt.lib

str$ MACRO any_text:VARARG
LOCAL txtname
.const
txtname db any_text,0
.data
EXITM <OFFSET txtname>
ENDM

SYSMETRIC_ENTRY STRUCT
    iIndex      DD ?
    labelText   DD ?
    descText    DD ?
SYSMETRIC_ENTRY    ENDS

.data
sysmetrics    LABEL SYSMETRIC_ENTRY
SYSMETRIC_ENTRY <0, str$("SM_CXSCREEN"), str$("Screen width in pixels")>
SYSMETRIC_ENTRY <1, str$("SM_CYSCREEN"), str$("Screen height in pixels")>

fmt db "%s %s",10,0

.code
mainCRTStartup PROC C
    invoke printf, ADDR fmt, sysmetrics.labelText, sysmetrics.descText
    ret
mainCRTStartup ENDP

END
output:SM_CXSCREEN Screen width in pixels
Title: Re: Win32 My First Window
Post by: NoCforMe on May 05, 2024, 07:00:06 PM
Yay! It works.

OP: Use Timo's macro instead (below). Much simpler than sinsi's. Use str$("text") in your structure.

Quote from: TimoVJL on May 05, 2024, 06:48:37 PMstr$ MACRO any_text:VARARG
LOCAL txtname
.const
txtname db any_text,0
.data
EXITM <OFFSET txtname>
ENDM
Even I can understand what's going on there.
Title: Re: Win32 My First Window
Post by: C3 on May 05, 2024, 07:52:53 PM
Quote from: NoCforMe on May 05, 2024, 12:39:41 PM
Quote from: sinsi on May 05, 2024, 12:27:05 PM
Quote from: NoCforMe on May 05, 2024, 12:22:45 PMTested?
yep (https://masm32.com/board/index.php?msg=130184)
Is .const a different initialized data area, separate from .data?
Pardon my ignorance; never heard of that nor used it.

Read only memory segment.
Title: Re: Win32 My First Window
Post by: tda0626 on May 05, 2024, 11:38:20 PM
Quote from: NoCforMe on May 05, 2024, 07:00:06 PMYay! It works.

OP: Use Timo's macro instead (below). Much simpler than sinsi's. Use str$("text") in your structure.

Quote from: TimoVJL on May 05, 2024, 06:48:37 PMstr$ MACRO any_text:VARARG
LOCAL txtname
.const
txtname db any_text,0
.data
EXITM <OFFSET txtname>
ENDM
Even I can understand what's going on there.



OK so like this?

str$ MACRO any_text:VARARG
LOCAL txtname
.const
txtname db any_text,0
.data
EXITM <OFFSET txtname>
ENDM

NUMLINES EQU  $sysmetricsSize / sizeof SYSMETRIC_ENTRY

SYSMETRIC_ENTRY STRUCT

            iIndex    DD ?
            szLabel   DD ?
            szDesc    DD ?
           
SYSMETRIC_ENTRY    ENDS





sysmetrics    LABEL SYSMETRIC_ENTRY

SYSMETRIC_ENTRY     <SM_CXSCREEN,    str$("SM_CXSCREEN"),    str$("Screen width in pixels")>
...

$sysmetricsSize    EQU $-sysmetrics
Title: Re: Win32 My First Window
Post by: tda0626 on May 06, 2024, 12:03:36 AM
Ok I got it to compile and link fine. The program pops up the window but it only prints the last  string entry and some other random garbage.

str$("Same color format flag")

Picture of window (https://drive.google.com/file/d/1n-VK7ay32mPBDfwhisIAcmvHqjn2qjwb/view?usp=sharing)

Source code is attached with the inc file.
Title: Re: Win32 My First Window
Post by: TimoVJL on May 06, 2024, 12:12:54 AM
I just tested sinsi's idea with .const (.rdata) with poasm.exe and after that with ml.exe and uasm32.exe
uasm.exe gives best error messages.
Title: Re: Win32 My First Window
Post by: jj2007 on May 06, 2024, 12:25:20 AM
Quote from: tda0626 on May 06, 2024, 12:03:36 AMthe window but it only prints the last  string

    local    cxChar:DWORD
    local    cxCaps:DWORD
    ; local    cyChar:DWORD
    local    hdc:HDC
    local    i:DWORD
    local    ps:PAINTSTRUCT   
    local    tm:TEXTMETRIC
    local    buffer_size:DWORD
    local    index:DWORD
    local    x:DWORD
    local    y:DWORD
    .DATA?
    cyChar dd ?
      .CODE

Remember that local variables are not permanent.
Title: Re: Win32 My First Window
Post by: tda0626 on May 06, 2024, 12:51:16 AM
Wow JJ, moved most of the locals to .data? and now it shows  all the data.  :thumbsup: I was thinking it was going to be something complicated with my multiple TextOut sections but never that simple.

Minor issue, if the string has a dash, '-', it doesn't print it correctly.Do you think it might be simple as using a format specifier?

Sysmets1 Working Image (https://drive.google.com/file/d/16wigD0EQZ0ss_G4LfuPxOhRFsDGwxnd4/view?usp=sharing)


Tim
Title: Re: Win32 My First Window
Post by: sudoku on May 06, 2024, 12:53:00 AM
Testing sinsi's pair of macros. Yes you need both! And it werks.
Commenting out either of the pair yields errors.  Must be used together.
Simpler version than originally posted here. (It used a register and 'assume', but opted for the simpler form as shown here).
        include \masm32\include\masm32rt.inc
cstr MACRO lbl,string:VARARG
LOCAL s,x,z
    IFDEF __UNICODE__
        align 2
        IFNB <lbl>
            lbl LABEL WORD
        ENDIF
        FOR s,<string>
            IFIDN @SubStr(s,1,1),<">
                x SUBSTR <s>,2,(@SizeStr(s)-2)
                % FORC c,<x>
                     dw "&c"
                  ENDM
            ELSE
                dw s
            ENDIF
        ENDM
        dw 0
    ELSE
        IFNB <lbl>
            lbl LABEL BYTE
        ENDIF
        FOR s,<string>
            IFIDN @SubStr(s,1,1),<">
                x SUBSTR <s>,2,(@SizeStr(s)-2)
                % FORC c,<x>
                     db "&c"
                  ENDM
            ELSE
                db s
            ENDIF
        ENDM
        db 0
    ENDIF
ENDM

@cstr MACRO string:VARARG
LOCAL s,sss1
sss1 TEXTEQU @CurSeg
.const
cstr s,string
IFIDNI sss1,<_TEXT>
    .code
ELSEIFIDNI sss1,<_BSS>
    .data?
ELSEIFIDNI sss1,<_DATA>
    .data
ENDIF
    EXITM <offset s>
ENDM


RCT1 RECT <@cstr("hello world"), @cstr("masm32 rocks!"), 0, 0>

.code

start:

invoke MessageBox, 0, RCT1.left, RCT1.top, 0
invoke ExitProcess, 0

end start

Yes, I used a RECT structure for this example. We are not drawing with it, so why not. :biggrin:

Title: Re: Win32 My First Window
Post by: jj2007 on May 06, 2024, 01:52:56 AM
Quote from: tda0626 on May 06, 2024, 12:51:16 AMWow JJ, moved most of the locals to .data? and now it shows  all the data.

Locals are fine if you use them inside the same message, i.e. the same handler. Their values are lost when you hit return, but inside the WndProc they are ok.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 06, 2024, 05:02:27 AM
Quote from: jj2007 on May 06, 2024, 01:52:56 AMLocals are fine if you use them inside the same message, i.e. the same handler. Their values are lost when you hit return, but inside the WndProc they are ok.
To be more precise: "their (local) values are lost when the routine they're in exits.". Also known as "going out of scope".

Easy to understand why this is: because local variables are allocated on the stack, every time you enter a routine you get whatever stack space happens to be available (meaning that ESP, the stack pointer, will be at some random, though DWORD-aligned, place). So whatever is on the stack when you enter the routine is what you get in any local variable, AKA "garbage". Could be the previous routine's return address, a pointer to something, a temp variable, anything.

Some high-level languages might clear locals to zero on entry to a routine, but one thing you can be sure of is that you can't rely on locals retaining their values from one entry into a routine to another. You might get lucky and find an existing value still there, but don't count on that!

Dunno what hitting return has to do with it. Ah, I get it; I thought you meant "hitting <Return>". You mean reaching the RET of the subroutine. Got it.
Title: Re: Win32 My First Window
Post by: jj2007 on May 06, 2024, 06:44:01 AM
Quote from: jj2007 on May 06, 2024, 01:52:56 AMTheir values are lost when you hit return

Quote from: NoCforMe on May 06, 2024, 05:02:27 AMtheir (local) values are lost when the routine they're in exits

Apart from returning, are there other ways to exit a proc?
Title: Re: Win32 My First Window
Post by: NoCforMe on May 06, 2024, 06:49:50 AM
Sure:
    XOR  ECX, ECX
    DIV  ECX
Title: Re: Win32 My First Window
Post by: tda0626 on May 06, 2024, 07:14:03 AM
Quote from: tda0626 on May 06, 2024, 12:51:16 AMMinor issue, if the string has a dash, '-', it doesn't print it correctly.Do you think it might be simple as using a format specifier?


Never mind, I figured it out. When I was coping from the book, the dash in the book is different than an ASCII dash. Replaced all those in my inc file and now it shows it fine.

 
Title: Re: Win32 My First Window
Post by: NoCforMe on May 06, 2024, 07:18:29 AM
Quote from: tda0626 on May 06, 2024, 12:51:16 AMMinor issue, if the string has a dash, '-', it doesn't print it correctly.
The problem is your source (the file sysmets.inc): all the hyphen characters in that file aren't just regular ASCII hyphens, but some kind of Unicode character (maybe an em dash?) that doesn't render correctly. Just go through the file with a regular ASCII text editor and change them all to "normal" hyphens.

I see you answered your own question. Good going!

And congrats on getting this working.
Title: Re: Win32 My First Window
Post by: sudoku on May 06, 2024, 07:28:57 AM
Congrats, tda0626  :thumbsup:

Hopefully Petzolds next project will be a little more straightforward.  :biggrin:
Title: Re: Win32 My First Window
Post by: tda0626 on May 06, 2024, 07:58:16 AM
Quick question, how would you handle the C Break statement in assembler? Like a jmp to the DefWndProc function?

case WM_VSCROLL:
 switch (LOWORD (wParam))
 {
 case SB_LINEUP:
 iVscrollPos −= 1 ;
 break ;
 case SB_LINEDOWN:
 iVscrollPos += 1 ;
 break ;
 case SB_PAGEUP:
 iVscrollPos −= cyClient / cyChar ;
 break ;
 case SB_PAGEDOWN:
 iVscrollPos += cyClient / cyChar ;
 break ;
 case SB_THUMBPOSITION:
 iVscrollPos = HIWORD (wParam) ;
 break ;
 default :
 break ;
 }
Title: Re: Win32 My First Window
Post by: jj2007 on May 06, 2024, 07:59:39 AM
Quote from: NoCforMe on May 06, 2024, 06:49:50 AMSure:
    XOR  ECX, ECX
    DIV  ECX

Why is there no smiley function in this forum?  :biggrin:

Quote from: tda0626 on May 06, 2024, 07:58:16 AMhow would you handle the C Break statement in assembler?

No need for action - there is no fall through in assembly. That "break;" is a horrible C abomination.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 06, 2024, 08:03:11 AM
There's some controversy about this (see another thread somewhere here on this topic).
However, if you read the documentation for the WM_VSCROLL message (https://learn.microsoft.com/en-us/windows/win32/controls/wm-vscroll), it says:
QuoteIf an application processes this message, it should return zero.
Therefore, the right thing to do in that handler would be to return zero if you handle the message, meaning that you want no more processing done on it.
Otherwise, jump to the DefWindowProc() function.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 06, 2024, 08:05:11 AM
Quote from: jj2007 on May 06, 2024, 07:59:39 AM
Quote from: NoCforMe on May 06, 2024, 06:49:50 AMSure:
    XOR  ECX, ECX
    DIV  ECX

Why is there no smiley function in this forum?  :biggrin:
You know full well, Jochen, that I abhor the use of those stupid smileys and emoticons.
All they do is dumb down people's comprehension.
If someone's too dense to pick up on irony, that's their problem.
Title: Re: Win32 My First Window
Post by: tda0626 on May 06, 2024, 08:35:56 AM
Quote from: jj2007 on May 06, 2024, 07:59:39 AMNo need for action - there is no fall through in assembly. That "break;" is a horrible C abomination.


Got it.


Quote from: NoCforMe on May 06, 2024, 08:03:11 AMThere's some controversy about this (see another thread somewhere here on this topic).
However, if you read the documentation for the WM_VSCROLL message (https://learn.microsoft.com/en-us/windows/win32/controls/wm-vscroll), it says:
QuoteIf an application processes this message, it should return zero.
Therefore, the right thing to do in that handler would be to return zero if you handle the message, meaning that you want no more processing done on it.
Otherwise, jump to the DefWindowProc() function.

Petzold had a separate switch case to process wParam that is triggered with WM_VSCROLL, which later on returns 0.
Title: Re: Win32 My First Window
Post by: tda0626 on May 06, 2024, 09:35:49 AM
I am doing the scroll bar part of sysmets1 program from Petzold's book and added my code to sysmets1. When I run it, the scrollbar is shown along with the thumb. However, it does not do squat. It will move the thumb up and down but does not affect the client window contents. Also when I reach verttop the thumb wraps around to vertbottom but not the other way around.

Also, how would I go about debugging these window messages? I know like the bare minimum basics when using a debugger.


My source is attached along with the inc.

Tim
Title: Re: Win32 My First Window
Post by: jj2007 on May 06, 2024, 09:44:02 AM
Quote from: NoCforMe on May 06, 2024, 08:05:11 AMYou know full well, Jochen, that I abhor the use of those stupid smileys and emoticons

You are a free citizen in a free land, pardon: Internet. Don't use them if you don't like them.

P.S.: Try to sue SMF for forcing 39 stupid smileys and emoticons on you :cool:
Title: Re: Win32 My First Window
Post by: NoCforMe on May 06, 2024, 09:51:12 AM
One thing I noticed is that you created your window without the WS_VSCROLL style. (I'm looking at his SYSMETS2 program, which includes that style for the window.) But you say you see a scrollbar ...
invoke CreateWindowExA,\
WS_EX_LEFT,\
offset szAppName,\
offset win_caption,\
WS_OVERLAPPEDWINDOW  OR WS_VSCROLL,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
0,\
NULL,\
hinstance,\
NULL
Title: Re: Win32 My First Window
Post by: NoCforMe on May 06, 2024, 09:55:14 AM
Couple little details:
o INVOKE doesn't need those stupid line-continuation ("\") characters, so you can lose those.
o WS_EX_LEFT is one of those stupid style constants that = 0, so does nothing, so you can lose that as well.
Title: Re: Win32 My First Window
Post by: jj2007 on May 06, 2024, 09:56:09 AM
Quote from: tda0626 on May 06, 2024, 09:35:49 AMAlso, how would I go about debugging these window messages? I know like the bare minimum basics when using a debugger.

Debugging the WndProc is close to impossible with a debugger - too much interaction between the debug window and the WndProc itself. Try the attached executable, built on your minimally modified source. You won't see the following messages because they appear too often:
WM_MOUSEMOVE, WM_NCHITTEST, WM_SETCURSOR, WM_GETICON, WM_NCMOUSEMOVE, WM_MOVING, WM_ENTERIDLE
Title: Re: Win32 My First Window
Post by: NoCforMe on May 06, 2024, 10:01:20 AM
Quote from: tda0626 on May 06, 2024, 09:35:49 AMAlso, how would I go about debugging these window messages? I know like the bare minimum basics when using a debugger.
As JJ pointed out, a debugger is of little help here. (There are so many messages flying around that if you set a breakpoint on a window proc, you'll be there forever hitting F9 until you maybe see the message you're looking for.)

However, I have a tool I created that might be of use to you. It's called LogBuddy, and it captures messages went to a window (among other things) and displays the messages and their contents in a logging window. You can filter messages to narrow down what you're looking at.

If this sounds interesting to you I'll be happy to post the package here. It involves a little bit of adding stuff to your program to be logged, not too much, plus there's a DLL. Probably 15 minutes of work before you can see what messages are coming into your proc.
Title: Re: Win32 My First Window
Post by: sinsi on May 06, 2024, 12:49:04 PM
Possibly overkill, but VS 2022 has a tool called SpyXX which lets you view all sorts of info about a window including messages.
Mine lives in C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools
Title: Re: Win32 My First Window
Post by: NoCforMe on May 06, 2024, 01:10:18 PM
Well, I'd like it if the OP is curious enough about my utility to try it. I've been hoping for unsuspecting victims trial users for it.
Title: Re: Win32 My First Window
Post by: jj2007 on May 06, 2024, 04:46:28 PM
Quote from: sinsi on May 06, 2024, 12:49:04 PMVS 2022 has a tool called SpyXX

Unfortunately non on my machine. There is a SOF debate about that (https://stackoverflow.com/questions/1811019/i-want-spy-but-i-dont-have-visual-studio).

I am quite used to deb (https://www.jj2007.eu/MasmBasicQuickReference.htm#Mb1019), which produces this kind of output (you may note gaps in the numbering, many messages are filtered):

        #  wParam  lParam      uMsg
msg    1  00000000 0019FA9C    WM_GETMINMAXINFO
msg    2  00000000 0019FA90    WM_NCCREATE
msg    3  00000000 0019FA7C    WM_NCCALCSIZE
msg    4  00000000 0019FA90    WM_CREATE
msg    5  00000000 0019F6EC    WM_WINDOWPOSCHANGING
msg    6  00000001 0019F6C4    WM_NCCALCSIZE
msg    7  00000000 0019F6EC    WM_WINDOWPOSCHANGED
msg    8  00000000 02310237    WM_SIZE
msg    12  00000001 00000000  WM_SHOWWINDOW
msg    13  00000000 0019FEC4  WM_WINDOWPOSCHANGING
msg    14  00000000 0019FEC4  WM_WINDOWPOSCHANGING
msg    15  00000001 00001C40  WM_ACTIVATEAPP
msg    16  00000001 00000000  WM_NCACTIVATE
msg    17  00000001 00000000  WM_ACTIVATE
msg    18  00000001 C000000F  WM_IME_SETCONTEXT
msg    19  00000002 00000000  WM_IME_NOTIFY
msg    20  00000000 00000000  WM_SETFOCUS
msg    21  00000001 00000000  WM_NCPAINT
msg    22  6C01156C 00000000  WM_ERASEBKGND
msg    23  00000000 0019FEC4  WM_WINDOWPOSCHANGED
msg    24  00000000 02310237  WM_SIZE
msg    25  00000000 00510260  WM_MOVE
msg    26  00000000 00000000  WM_PAINT
msg    28  00000001 00000000  WM_DWMNCRENDERINGCHANGED
msg    29  00000000 00000000  TaskbarButtonCreated
msg    30  00000000 0019FE84  WM_GETMINMAXINFO
msg    58  00000000 00000000  WM_NCMOUSELEAVE
msg    177  00000000 00000000  WM_NCMOUSELEAVE
msg    258  00000001 00E50029  WM_LBUTTONDOWN
msg    261  00000000 00E50029  WM_LBUTTONUP
msg    286  00000000 00000000  WM_NCMOUSELEAVE
msg    385  00000001 00850045  WM_LBUTTONDOWN
msg    388  00000000 00850045  WM_LBUTTONUP
msg    391  00000001 00850045  WM_LBUTTONDOWN
msg    394  00000000 00850045  WM_LBUTTONUP
msg    414  00000000 00000000  WM_NCACTIVATE
msg    415  00000000 00000000  WM_ACTIVATE
msg    416  00000000 00001C40  WM_ACTIVATEAPP
msg    417  00000000 00000000  WM_KILLFOCUS
msg    418  00000000 C000000F  WM_IME_SETCONTEXT
msg    419  00000001 00000000  WM_IME_NOTIFY
...

It's a two-liner:
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
local    hdc:HDC
local    ps:PAINTSTRUCT   
local    tm:TEXTMETRIC
  inc msgCount
  deb 4, "msg", chg:msgCount
    SWITCH    uMsg

With NoDebMsg CATSTR NoDebMsg, <, WM_ENTERIDLE, WM_WHATEVER> you can add filters.
With NoDebMsg equ <>, you get all messages.
Use deb 5 instead of deb 4 to send all output to a DebLog.txt file in the executable's folder.

Attached an unfiltered dump, with lots of WM_GETICON, WM_NCHITTEST and WM_SETCURSOR.

Never seen WM_DWMNCRENDERINGCHANGED (https://learn.microsoft.com/en-us/windows/win32/dwm/wm-dwmncrenderingchanged) before :biggrin:
Title: Re: Win32 My First Window
Post by: tda0626 on May 07, 2024, 07:55:14 AM
Petzold has this little bit of C code in his book.

iVscrollPos = max (0, min (iVscrollPos, NUMLINES − 1))

I am assuming those are macros. Looking them up

max is defined as #define max(a, b)  (((a) > (b)) ? (a) : (b))
min is defined as #define min(a, b)  (((a) < (b)) ? (a) : (b))


So this is what I got out of it. Could someone check it and see if the logic follows the define statements above?

mov eax, NUMLINES
sub eax, 1

.if iVscrollPos < eax

mov eax, iVscrollPos

.else

mov eax, NUMLINES - 1

.endif

mov ecx, 0

.if ecx > eax

mov iVscrollPos, 0

.else

mov iVscrollPos, eax

.endif
Title: Re: Win32 My First Window
Post by: NoCforMe on May 07, 2024, 08:05:11 AM
Can simplify it a bit:
mov eax, NUMLINES
sub eax, 1
.if iVscrollPos < eax
mov eax, iVscrollPos
; .else ;Not needed
; mov eax, NUMLINES - 1
.endif
because you already set EAX to NUMLINES-1.
Title: Re: Win32 My First Window
Post by: jj2007 on May 07, 2024, 08:43:10 AM
Quote from: NoCforMe on May 07, 2024, 08:05:11 AMCan simplify it a bit:
mov eax, NUMLINES-1
.if iVscrollPos < eax
mov eax, iVscrollPos
; .else ;Not needed
; mov eax, NUMLINES - 1
.endif
Title: Re: Win32 My First Window
Post by: tda0626 on May 07, 2024, 08:44:20 AM
Fixed the thumb wrap around by checking to see if eax==0. Now it stops at the top.
My page up/down do not work. Clicking the page up/down causes the thumb to go to the bottom of the scrollbar. Can't figure out what is happening.



        CASE            WM_CREATE
                        mov eax, tm.tmHeight
            mov edx, tm.tmExternalLeading
            add eax, edx                        ; tm.tmHeight + tm.tmExternalLeading
            mov cyChar, eax                                 

        CASE    WM_SIZE
           
            mov eax, lParam
            and eax, 0FFFF0000h        ; HIWORD lParam
            mov cyClient, eax
            ret



CASE SB_PAGEUP
                   
                    xor edx, edx
                    mov eax, cyClient
                    div cyChar
                    sub  eax, iVscrollPos
                    mov iVscrollPos, eax
                   
                   
                CASE SB_PAGEDOWN
                   
                    xor edx, edx
                    mov eax, cyClient
                    div cyChar
                    add eax, iVscrollPos
                    mov iVscrollPos, eax

Added Petzold's code for reference...

case SB_PAGEUP:
 iVscrollPos −= cyClient / cyChar ;
 break ;
 case SB_PAGEDOWN:
 iVscrollPos += cyClient / cyChar ;
 break ;
Title: Re: Win32 My First Window
Post by: NoCforMe on May 07, 2024, 09:21:37 AM
I think you have your subtract backwards in the SB_PAGEUP case:
C:
 iVscrollPos -= cyClient / cyChar ;

asm:
MOV EAX, cyClient
XOR EDX, EDX
DIV cyChar
SUB iVscrollPos, EAX
Title: Re: Win32 My First Window
Post by: tda0626 on May 10, 2024, 09:25:10 AM
So I got the scrollbar pageup, pagedown, thumbposition, linedown, and lineup to work or it appears like they are working but the window client area does nothing when doing something with the scrollbar. Could someone look at my code and help me out? Been at this for hours now...

My source is attached with the inc file.

Title: Re: Win32 My First Window
Post by: TimoVJL on May 10, 2024, 05:13:35 PM
In SysMets2.c
        iVscrollPos = max(0, min(iVscrollPos, NUMLINES - 1));

        if (iVscrollPos != GetScrollPos(hwnd, SB_VERT))
        {
            SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);
            InvalidateRect(hwnd, NULL, TRUE);
        }
        return 0;
this part of code have to process, as break lead to it in WM_VSCROLL handling.
In debugger put breakpoint to it, just to see why InvalidateRect isn't called.

Also check, printing loop, that it starts with right value.
is this handled ?y = cyChar * (i - iVscrollPos);
Title: Re: Win32 My First Window
Post by: tda0626 on May 11, 2024, 01:15:08 AM
Quote from: TimoVJL on May 10, 2024, 05:13:35 PMAlso check, printing loop, that it starts with right value.
is this handled ?y = cyChar * (i - iVscrollPos);

Ahh, that little bit of code was overlooked. I will have to change it when I get home to see if that was the problem. Thank you for the help! It is much appreciated!


Tim
Title: Re: Win32 My First Window
Post by: tda0626 on May 11, 2024, 07:39:37 AM
Yea that was it. I made that change and it worked but it was scrolling only the szLabel portion. After reviewing my code, I had this bit in there before the other two calls to TextOut.

                        mov eax, cyChar
mul i
mov y, eax

Don't know why I had that in there but I took both instances of it out and now it scrolls all the text at the same time. Awesome! Thanks again for your help!  :biggrin:
Title: Re: Win32 My First Window
Post by: NoCforMe on May 11, 2024, 08:32:43 AM
Congrats on your progress.

I just have a general comment about your programming efforts. Looking at some of your comments in your code, it seems to me that you don't really understand what's going on in certain parts of it.

Example: you have this in your WM_CREATE handler where you're calculating text character dimensions:
xor eax, eax
mov al, tm.tmPitchAndFamily ; move tm.tmPitchAndFamily in al because tm.tmPitchAndFamily is a byte value
and eax, 1 ; tm.tmPitchAndFamily & 1

;--------------------------------------------------------------------------------------------------------
; Conditional tm.tmPitchAndFamily & 1 ? 3 : 2 = Checks if value if not zero then use 3 or zero then use 2
;--------------------------------------------------------------------------------------------------------

.if eax == 0 ; if zero then use 2 but since it is a divide by 2
; and then multiply by 2 it is like multiplying cxChar by 1

;--------------------------------------------------------------------------------------
; Step 1: move value of cxChar into eax.
; Step 2: Divide eax by 2.
; Step 3: add eax and cxChar to multiply by 3.
; Step 4: Check if the number is odd by checking to see if bit zero is turned on. If odd, then subtract 1.
; Step 5: move result into cxCaps
;---------------------------------------------------------------------------------------

(First of all I had to do massive reformatting on your text because it's spread out so wide. You must have a huuuuge monitor or something! Makes it very hard for other people to read your code.)

But it's clear from that snippet that you don't get what Petzold is doing there. It has nothing to do with odd or even. What he's doing here is calculating the average width of uppercase characters. He even describes it pretty clearly:
QuoteSYSMETS1 also saves an average width of uppercase letters in the static variable cxCaps. For a fixed-pitch font, cxCaps would equal cxChar. for a variable-width font, cxCaps is set to 150 percent of cxChar. The low bit of the tmPitchAndFamily field in the TEXTMETRIC structure is 1 for a variable-width font and 0 for a fixed-pitch font. SYSMETS1 uses this bit to calculate cxCaps from cxChar:

cxCaps = (tm.tmPitchAndFamily & 1 ? 3: 2) * cxChar / 2;

So that's what the 1 and the 2 and the 3 are for, coming up with either 100% or 150% of cxChar.

Now maybe I'm wrong about this and you do understand what's going on here, in which case I apologize.

I point this out not to ding you for being a bad programmer but to help you become a better one. Again, you really need to understand what's going on in any piece of code before you borrow it or translate it from C to assembly language. Otherwise you're just kind of floundering around in the dark. If you really don't get something after scratching your head, you can always ask here.

Couple other little things; instead of
mov eax, lParam
and eax, 0ffff0000h
shr eax, 010h
mov cyClient, eax
there's a very useful X86 instruction called MOVZX (or MOVSX to preserve the sign of the operand) custom-made for this which puts a byte or a word into a doubleword register in one fell swoop:
movzx eax, WORD PTR lParam
mov cyClient, EAX

I notice you tend to use this sequence a lot to set variables:
push 0
pop wndclass.cbWndExtra
While that's perfectly legal and OK, why not just do this?
mov wndclass.cbWndExtra, 0

Title: Re: Win32 My First Window
Post by: NoCforMe on May 11, 2024, 08:56:04 AM
Sorry, my bad: that code should be
movzx eax, WORD PTR lParam + 2
mov cyClient, EAX
since you're after the high word of lParam here.
Title: Re: Win32 My First Window
Post by: tda0626 on May 11, 2024, 10:32:19 AM
I do understand most of the code but there are portions of it that I will have revisit and analyze. I will keep at it though and eventually it will come to me.

I did some reading on what LOWORD and HIWORD were and were going by how they were defined but it is nice to know a shortcut like movzx. That would have made my life a little easier but it was nice to use the some bitwise operations just for the practice. My memory is fuzzy but I remember doing bitwise or on ASCII letters to make them lower or upper case and some simple xor encryption back in the day along with some other stuff.

Don't have a massive monitor, just a 27".

I am working on sysmets3 now but when I assemble it, it doesn't recognize the SCROLLINFO struct members. I got a crap ton of undefined symbol errors for nPos, cbSize, ect.. I checked the windows.inc and it is defined in there with those members but masm for some reason doesn't make the connection. Anyone know why?

Defined it in my window procedure like this just like a did with the Paint struct and Text Metric struct.
local si:SCROLLINFO
Title: Re: Win32 My First Window
Post by: NoCforMe on May 11, 2024, 10:56:37 AM
Can't use si; that's the name of a register (16-bit lower half of ESI).
Try a different name.

To make a character (in AL) uppercase:
    AND   AL, 5FH
To make it lowercase:
    OR    AL, 20H
(Interesting; 20H is the space character)
Title: Re: Win32 My First Window
Post by: tda0626 on May 11, 2024, 11:23:45 AM
Quote from: NoCforMe on May 11, 2024, 10:56:37 AMCan't use si; that's the name of a register (16-bit lower half of ESI).
Try a different name.


Ok now I feel stupid. lol Can't believe I didn't see that.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 11, 2024, 11:50:46 AM
Quote from: tda0626 on May 11, 2024, 11:23:45 AM
Quote from: NoCforMe on May 11, 2024, 10:56:37 AMCan't use si; that's the name of a register (16-bit lower half of ESI).
Try a different name.

Ok now I feel stupid. lol Can't believe I didn't see that.

Be kind to yourself.
You're at the stage where you're learning by making mistakes. (Just try not to make the same mistake more than 2 or 3 times!)

That's actually the best way to learn.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 11, 2024, 12:59:03 PM
Just a note about names:
You translated Petzold's SYSMETS3 program thinking you could use the same names he did. Now in C, si is nothing special, but in MASM it's a reserved word (a register name).

Likewise, there are other "gotchas" to be aware of. For instance, if you ever play around with treeview controls, you'll be using a structure called TVITEM:
typedef struct tagTVITEM {
  UINT      mask;
  HTREEITEM hItem;
  UINT      state;
  UINT      stateMask;
  LPSTR    pszText;
  int      cchTextMax;
  int      iImage;
  int      iSelectedImage;
  int      cChildren;
  LPARAM    lParam;
} TVITEM, *LPTVITEM;

Problem is, "mask" is also a reserved word in MASM, so in the windows.inc file this member has been renamed to _mask:
TVITEMA STRUCT
  _mask            DWORD  ?
  hItem            DWORD  ?
  state            DWORD  ?
  stateMask        DWORD  ?
  pszText          DWORD  ?
  cchTextMax        DWORD  ?
  iImage            DWORD  ?
  iSelectedImage    DWORD  ?
  cChildren        DWORD  ?
  lParam            DWORD  ?
TVITEMA ENDS
Other than this, almost all the names in windows.inc are identical to the Microsoft-defined names, but now and then they aren't, so if you get errors, you might want to take a peek at the include file and see what's what.

And a note about that name, TVITEMA: the "A" is for ANSI, since this is the ANSI version of that structure. The Unicode version is called TVITEMW ("W" for "wide").
Rather than use these names, I just use the generic version of the structure which is TVITEM and let the assembler select the correct actual version (either "A" or "W"; all my code uses ANSI, so I never deal with the Unicode stuff.)
Title: Re: Win32 My First Window
Post by: tda0626 on May 13, 2024, 04:56:10 AM
Thanks, I will keep an eye out for things like that.

I came across this site that writes code to what you specify in x86 assembly and had it create a program like sysmets3. It was interesting to see the AI's take on how it handles printing the info to the screen and adding the scrollbars. There were a crap ton of errors when assembling the code but I finally did get it to assemble and link but it doesn't work.  :biggrin: One thing I liked, is how the AI split the HORZ and VERT scrolling handling in separate functions. I will see if I can get it to work since it is easier to follow. 


Tim
Title: Re: Win32 My First Window
Post by: sudoku on May 13, 2024, 05:49:33 AM
Quote from: tda0626 on May 13, 2024, 04:56:10 AMI came across this site that writes code to what you specify in x86 assembly and had it create a program like sysmets3.
Okay, fantastic!  :biggrin:


QuoteThere were a crap ton of errors when assembling the code but I finally did get it to assemble and link but it doesn't work.
:undecided:  Sorry to hear that, but not unexpected either. You could possibly post the AI generated code, to see if someone here can help you debug it (to find out why it is not working).
Title: Re: Win32 My First Window
Post by: NoCforMe on May 13, 2024, 06:26:41 AM
Wellll, my advice is, don't trust that AI much farther than you can throw it.
Really. It just ain't ready for prime time.
Besides, for you it's much better not to rely on a cheating tool like that, if your goal is to actually learn x86 assembly.
Otherwise, if all you want is a cheap and easy way to convert stuff without having to understand it (or without even necessarily producing valid code), go for it.
Title: Re: Win32 My First Window
Post by: tda0626 on May 13, 2024, 06:57:12 AM
Quote from: NoCforMe on May 13, 2024, 06:26:41 AMOtherwise, if all you want is a cheap and easy way to convert stuff without having to understand it (or without even necessarily producing valid code), go for it.


Just wanted to see what it would do when faced with the same problem. It is ok, I still plan on learning the old fashioned way if that makes you feel better.  :cool:

AI is a tool just like a calculator is a tool but is not a substitute for learning, which I am well aware of. Taking Calc 2 in college back in the day, I did everything on paper even though it took me hours to do like 6 problems and several sheets of paper despite having a calculator in reach that would solve those equations in a split second. Instead, I used the calculator to check my math to see if it was right. Granted, what the calculator gave as an answer and what answer I had was not necessarily in the same format, which made it a little difficult to check my homework sometimes. But yea, I get it.





Quote:undecided:  Sorry to hear that, but not unexpected either. You could possibly post the AI generated code, to see if someone here can help you debug it (to find out why it is not working).

If you want to take a look at the source code it generated, it is attached. I had to modify some of it to get it to assemble but 95% is the AI code.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 13, 2024, 07:11:19 AM
Well, this is wrong right off the bat:
    .elseif uMsg == WM_PAINT
        invoke BeginPaint, hwnd, offset ps
        invoke DrawSystemMetrics, hDC
        invoke EndPaint, hwnd, offset ps
        xor eax, eax
        ret
I'll leave it to you to tell me what's wrong there.
Hint: DrawSystemMetrics() takes one parameter, hDC.
Title: Re: Win32 My First Window
Post by: tda0626 on May 13, 2024, 07:51:19 AM
The return value from BeginPaint, handle to device context, needs to be passed to the DrawSystemMetrics.

Title: Re: Win32 My First Window
Post by: NoCforMe on May 13, 2024, 08:14:26 AM
Bingo!

Extra credit exercise:
There are two ways you can do that. One involves an extra instruction, while the other one doesn't, just a change to a parameter: what are they?
Title: Re: Win32 My First Window
Post by: tda0626 on May 13, 2024, 09:09:08 AM
Removed hDC and just added eax since it already had the value so no need to do something like mov hDC, eax.
Title: Re: Win32 My First Window
Post by: NoCforMe on May 13, 2024, 09:21:11 AM
Yep, that's the easier way. Good job.

I hope you don't think I'm being patronizing: I really do think you're doing pretty well here in asm-land.