News:

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

Main Menu

Win32 My First Window

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

Previous topic - Next topic

TimoVJL

May the source be with you

NoCforMe

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.
Assembly language programming should be fun. That's why I do it.

tda0626

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.

NoCforMe

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).
Assembly language programming should be fun. That's why I do it.

zedd151

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.

tda0626

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 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

TimoVJL

WinMain is just an entry point for languages, that have runtime libraries, like C/C++, Pascal, ...
The WinMain 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.
May the source be with you

jj2007

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$():
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 :cool:

See also 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.

NoCforMe

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.
Assembly language programming should be fun. That's why I do it.

zedd151

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

tda0626

Do I push them like this?
 

push 0
lea eax, CommandLine
push eax
push 0
push hinst
call WinMain

NoCforMe

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?)
Assembly language programming should be fun. That's why I do it.

TimoVJL

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.
May the source be with you

NoCforMe

@Timo:
Your post is 100% correct, and it gave me a chuckle on top of that.
Assembly language programming should be fun. That's why I do it.

C3

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.