Simple tutorial to create first Windows applications

Windows specific questions.
Xusinboy Bekchanov
Posts: 783
Joined: Jul 26, 2018 18:28

Re: Simple tutorial to create first Windows applications

Post by Xusinboy Bekchanov »

jj2007 wrote:
Xusinboy Bekchanov wrote:You need to select UTF8 in the status bar and paste this text, then it works.
Thanks, I had not seen this one. Seems to work, kind of - when clicking there, it chops off several bytes at the end of the document, but Ctrl Z restores them. Macq's unmodified code works then with Ansi but not with the two BOM modes.
Yes, only in ANSI. Also need to fix line 116, as mentioned above:
if (not CInt(hPrevInstance)) then:
And it compiles in 64 bit mode too
Macq
Posts: 24
Joined: Feb 18, 2021 4:01
Location: Queensland, Australia

Re: Simple tutorial to create first Windows applications

Post by Macq »

This is me learning to use a vertical scroll bar and SetScrollInfo() to scroll some text.
Use left mouse button or Enter(Auto repeat) to add lines of text.
Use mouse pointer, mouse wheel, page up, page down, up arrow, down arrow to scroll.
The text display also attempts at adapt to the vertical window size changing.
Written in WinFBE 2.1.8(64-bit) ANSI.

Code: Select all

' Macq  TestScroll.bas

#include once "windows.bi"
#include once "crt.bi"      ' for sprintf
#define MAXLINES 200
#define WINDOWWIDTH1 550

 
' This is just an empty declaration.  See below for the definition of this fuction
Declare Function        WinMain     (ByVal hInstance As HINSTANCE, _
                                      ByVal hPrevInstance As HINSTANCE, _
                                      szCmdLine As String, _
                                      ByVal iCmdShow As Integer) As Integer
                                 
                                 

    End WinMain(GetModuleHandle(null), null, Command, SW_NORMAL)


'' ::::::::
'' name: WndProc
'' desc: Processes windows messages
''
'' ::::::::
Function WndProc (ByVal hw As HWND, _
                   ByVal message As UINT, _
                   ByVal wParam As WPARAM, _
                   ByVal lParam As LPARAM) As LRESULT
   
    static as string    lines(MAXLINES)
    Static as integer   colours(MAXLINES)
    static as integer   rainbow(6) = {&H0000FF,&H00A0FF,&H00F0F0,&H00FF00,&HFF8000,&HFF00B0,&HF000FF}
    static as integer   rotate = 0
    static as integer   index           ' current index into lines() and colours(0
    static as integer   yChar           ' Height of a character box in this window
    Static As integer   nDispLines      ' Number of lines that will fit in this window
    static as integer   VscrollPos = 0  ' The position of the scroll box in the "page" (si.nPos)
    static as boolean   ScrollBarEnabled = False
    Static as SCROLLINFO si
    Dim hdc As HDC  ' handle to device (screen) context
    
    lines(0) = "------Top-Line-------"
    colours(0) = rainbow(6)
   
    ''
    '' Process messages
    ''
    Select Case(message)
        ''
        '' Window was created
        ''        
        Case WM_CREATE
            dim as TEXTMETRIC tm            ' info about the current font for this window
            hdc = GetDC(hw)
            SelectObject(hdc,GetStockObject(OEM_FIXED_FONT))
            GetTextMetrics(hdc,@tm)
            yChar  = tm.tmHeight 
            ReleaseDC(hw,hdc)
            Exit Function

        case WM_SIZE
            VscrollPos = max(0,min(VscrollPos,index-nDispLines+1))
            InvalidateRect(hw,null,true) ' Cause re-paint
            exit function
            
        Case WM_MOUSEWHEEL
            ' wParam The high-order word indicates the distance the wheel is rotated.
            ' A positive value indicates that the wheel was rotated forward, away from the user
            ' wParam
            ' The low-order word indicates whether various virtual keys are down.
            ' lParam contains x,y of mouse pointer
            dim as integer temp, mousewheel = GET_WHEEL_DELTA_WPARAM(wParam)
            temp = VscrollPos             ' save a copy
            VscrollPos -= mousewheel / 40 ' One "notch" is 120 to allow for micro-wheel movements
            VscrollPos = max(0,min(VscrollPos,index-nDispLines+1)) ' Possibly move 3 lines, up or down. 120/40 = 3 :-)
            if(VscrollPos <> temp) then
                InvalidateRect(hw,null,true) ' Cause re-paint
            endif
            Exit function
            
        Case WM_VSCROLL ' User is clicking or dragging the vertical scroll bar
            dim as integer temp = VscrollPos ' save a copy
            select case LoWord(wParam)
                case SB_LINEUP
                    VscrollPos -= 1
                case SB_LINEDOWN
                    VscrollPos += 1
                case SB_PAGEUP
                    VscrollPos -= (nDispLines - 1)
                case SB_PAGEDOWN
                    VscrollPos += (nDispLines - 1)
                case SB_THUMBTRACK
                    VscrollPos = Hiword(wParam)
                case SB_THUMBPOSITION
                    VscrollPos = Hiword(wParam)
            end select
            VscrollPos = max(0,min(VscrollPos,index-nDispLines+1))
            if(VscrollPos <> temp) then
                InvalidateRect(hw,null,true) ' Cause re-paint
            endif
            Exit function
            
        ''
        '' Windows is being repainted
        ''
        'Case WM_COMMAND ' A Menu Item was selected

        Case WM_PAINT
            Dim As RECT rct
            Dim As PAINTSTRUCT ps

            GetClientRect(hw, @rct)
            nDispLines = (rct.bottom / yChar) - 1 
            if((not ScrollBarEnabled) and ((index+1)>nDispLines)) then
                ScrollBarEnabled = true
                EnableScrollBar(hw,SB_VERT,ESB_ENABLE_BOTH) ' Verticle scroll bar with both arrows
                ShowScrollBar(hw,SB_VERT,true)
            endif
            '-------------------------
            if(ScrollBarEnabled) then
                si.cbSize = SizeOf(si)
                si.fMask  = SIF_RANGE or SIF_PAGE Or SIF_POS
                si.nMin   = 0
                si.nMax   = index
                si.nPage  = nDispLines
                si.nPos   = VscrollPos
                SetScrollInfo(hw,SB_VERT,@si,true)
                'print "yChar =";yChar ,"nDispLines=";nDispLines,"VscrollPos@si=";VscrollPos,"index=";index
            endif

            '===========
            'Begin Paint
            '===========
            hdc = BeginPaint(hw, @ps)
            SelectObject(hdc,GetStockObject(OEM_FIXED_FONT))
            SetBkMode(hdc,TRANSPARENT)
            'SetTextColor(hdc,&HF0F0F0)
            for i as integer = VscrollPos to min(index,VscrollPos+nDispLines-1)
                SetTextColor(hdc,colours(i))
                'print "i=";i,"colours(i)=";colours(i)
                DrawText(hdc,lines(i),-1,@rct,DT_LEFT)
                rct.top += yChar
            next i
            'print "yChar=";yChar ,"nDispLines=";nDispLines,"VscrollPos=";VscrollPos
            EndPaint(hw, @ps)
           
            Exit Function            
       
        ''
        '' Key pressed
        ''
        Case WM_KEYDOWN
            dim as integer temp = VscrollPos ' save a copy
            select case lobyte(wParam)
                case VK_ESCAPE
                    PostMessage(hw,WM_QUIT,0,0) 'Close our main window, if Esc key pressed
                case VK_UP
                    VscrollPos -= 1
                case VK_DOWN
                    VscrollPos += 1
                case VK_PRIOR
                    VscrollPos -= (nDispLines - 1)
                case VK_NEXT
                    VscrollPos += (nDispLines - 1)
                case VK_RETURN
                    index += 1
                    if(index > MAXLINES) then index = MAXLINES
                    dim as string * 64  buf
                    sprintf(buf,"This is key   line %3d,   index=%3d",index+1,index)
                    lines(index) = buf
                    colours(index) = rainbow(rotate) : rotate = (rotate + 1) mod 7
                    InvalidateRect(hw,null,true) ' Cause re-paint
            end select
            VscrollPos = max(0,min(VscrollPos,index-nDispLines+1))
            if(VscrollPos <> temp) then
                InvalidateRect(hw,null,true) ' Cause re-paint
            endif
            exit function

        ''
        '' User clicked the form
        Case WM_LBUTTONDOWN
            index += 1
            if(index > MAXLINES) then index = MAXLINES
            dim as string * 64  buf
            sprintf(buf,"This is mouse line %3d,   index=%3d",index+1,index)
            lines(index) = buf
            colours(index) = rainbow(rotate) : rotate = (rotate + 1) mod 7
            InvalidateRect(hw,null,true) ' Cause re-paint
            exit function
        ''
        '' Window was closed
        ''
        Case WM_DESTROY
            PostQuitMessage(0)
            Exit Function
    End Select
   
    ''
    '' Message doesn't concern us, send it to the default handler
    '' and get result
    ''
    Function = DefWindowProc(hw, message, wParam, lParam)    
   
End Function

'' ::::::::
'' name: WinMain
'' desc: A WIN32 GUI program entry point
''
'' ::::::::
Function WinMain (ByVal hInstance As HINSTANCE, _
                   ByVal hPrevInstance As HINSTANCE, _
                   szCmdLine As String, _
                   ByVal iCmdShow As Integer) As Integer    
     
    Dim wMsg As MSG
    Dim wcls As WNDCLASS    
    Dim szAppName As String
    Dim hWnd As HWND

    ' Read an icon from shell32.dll
    Dim As HANDLE hIconLib, hDll
    hIconLib = LoadLibrary("shell32")
    wcls.hIcon = LoadIcon(hIconLib,cptr(any ptr,44))   ' get an icon
    FreeLibrary(hIconLib)

    ''
    '' Setup window class
    ''
    szAppName = "TestScroll"
     
    With wcls
        .style         = CS_HREDRAW Or CS_VREDRAW
        .lpfnWndProc   = @WndProc
        .cbClsExtra    = 0
        .cbWndExtra    = 0
        .hInstance     = hInstance
        ''.hIcon         = LoadIcon(NULL, IDI_APPLICATION)
        .hCursor       = LoadCursor(NULL, IDC_ARROW)
        '.hbrBackground = GetStockObject(GRAY_BRUSH)
        .hbrBackground = CreateSolidBrush(&H610000)
        .lpszMenuName  = NULL
        .lpszClassName = strptr(szAppName)
    End With
         
    ''
    '' Register the window class    
    ''    
    If(RegisterClass(@wcls) = FALSE) Then
       MessageBox(null, "Failed to register wcls!", szAppName, MB_ICONERROR)
       Exit Function
    End If
   
    ''
    '' Create the window and show it
    ''
    dim rxy as RECT   
    SystemParametersInfo(SPI_GETWORKAREA,0,@rxy,0)
    Dim vsize as integer = rxy.bottom * 0.5 ' The virtical size of the main client area

    hWnd = CreateWindowEx(0, _
                           szAppName, _
                           "Testing Scroll", _
                           WS_TILEDWINDOW, _
                           (rxy.Right - WINDOWWIDTH1)/2, _
                           (rxy.bottom - vsize)/2, _
                           WINDOWWIDTH1, _
                           vsize, _
                           NULL, _
                           NULL, _
                           hInstance, _
                           NULL)
                         

    ShowWindow(hWnd, iCmdShow)
    ShowScrollBar(Hwnd,SB_VERT,false)
    UpdateWindow(hWnd)
     
    ''
    '' Process windows messages
    ''
    While(GetMessage(@wMsg, NULL, 0, 0) <> FALSE)    
        TranslateMessage(@wMsg)
        DispatchMessage(@wMsg)
    Wend
   
   
    ''
    '' Program has ended
    ''
    Function = wMsg.wParam

End Function
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Simple tutorial to create first Windows applications

Post by jj2007 »

Very well done, Macq! Some minor points:

1. You declare WndProc as a function. That's possible but very unusual, as nobody will ever use the returned value
2. Your generous use of global (static) variables at the entry to WndProc will not please everybody
3. Integers are 64 bits long in 64-bit code; what you need to make both the 32- and the 64-bit compiler happy are 32-bit long variables
4. The empty declaration of WinMain is not necessary; just put WinMain(GetModuleHandle(null), null, Command, SW_NORMAL) at the end of the source, as shown below
5. Gcc-64 complains about format '%d' expects argument of type 'int', but argument 3 has type 'long long int' in sprintf(). The web is full of contradicting suggestions, try your luck...:

Code: Select all

                    ' above: Static as long index
                    dim as uint index2=index
                    sprintf(buf, "This is key   line %3d,   index=%3d", index2+1, index2)
None of this works, but Gcc is happy if you eliminate the index+1. It looks like the ugly C++ typecasting disease. This works, but don't ask me why: sprintf(buf, "This is key line %3d, index=%3d", cast(long, index+1), index)

Same for .lpfnWndProc = cast(any ptr, @WndProc) ' cast eliminates the dumb warning suspicious pointer assignment

Here is a version that declares WndProc as a Sub, and uses longs where 32-bit values are enough:

Code: Select all

' Macq  TestScroll.bas

#include once "windows.bi"
#include once "crt.bi"      ' for sprintf
#define MAXLINES 200
#define WINDOWWIDTH1 550

'' ::::::::
'' name: WndProc
'' desc: Processes windows messages
''
'' ::::::::
Sub WndProc (ByVal hw As HWND, _
                   ByVal message As UINT, _
                   ByVal wParam As WPARAM, _
                   ByVal lParam As LPARAM)
   
    Static as string    lines(MAXLINES)
    Static as long   colours(MAXLINES)
    Static as long   rainbow(6) = {&H0000FF,&H00A0FF,&H00F0F0,&H00FF00,&HFF8000,&HFF00B0,&HF000FF}
    Static as long   rotate = 0
    Static as long   index           ' current index into lines() and colours(0); see sprintf
    Static as long   yChar           ' Height of a character box in this window
    Static as long   nDispLines      ' Number of lines that will fit in this window
    Static as long   VscrollPos = 0  ' The position of the scroll box in the "page" (si.nPos)
    Static as boolean   ScrollBarEnabled = False
    Static as SCROLLINFO si
    Dim hdc As HDC  ' handle to device (screen) context
    lines(0) = "------Top-Line-------"
    colours(0) = rainbow(6)
   
    ''
    '' Process messages
    ''
    Select Case(message)
        ''
        '' Window was created
        ''       
        Case WM_CREATE
            dim as TEXTMETRIC tm            ' info about the current font for this window
            hdc = GetDC(hw)
            SelectObject(hdc,GetStockObject(OEM_FIXED_FONT))
            GetTextMetrics(hdc,@tm)
            yChar  = tm.tmHeight
            ReleaseDC(hw,hdc)
            return

        case WM_SIZE
            VscrollPos = max(0,min(VscrollPos,index-nDispLines+1))
            InvalidateRect(hw,null,true) ' Cause re-paint
            return
           
        Case WM_MOUSEWHEEL
            ' wParam The high-order word indicates the distance the wheel is rotated.
            ' A positive value indicates that the wheel was rotated forward, away from the user
            ' wParam
            ' The low-order word indicates whether various virtual keys are down.
            ' lParam contains x,y of mouse pointer
            dim as integer temp, mousewheel = GET_WHEEL_DELTA_WPARAM(wParam)
            temp = VscrollPos             ' save a copy
            VscrollPos -= mousewheel / 40 ' One "notch" is 120 to allow for micro-wheel movements
            VscrollPos = max(0,min(VscrollPos,index-nDispLines+1)) ' Possibly move 3 lines, up or down. 120/40 = 3 :-)
            if(VscrollPos <> temp) then
                InvalidateRect(hw,null,true) ' Cause re-paint
            endif
            return
           
        Case WM_VSCROLL ' User is clicking or dragging the vertical scroll bar
            dim as integer temp = VscrollPos ' save a copy
            select case LoWord(wParam)
                case SB_LINEUP
                    VscrollPos -= 1
                case SB_LINEDOWN
                    VscrollPos += 1
                case SB_PAGEUP
                    VscrollPos -= (nDispLines - 1)
                case SB_PAGEDOWN
                    VscrollPos += (nDispLines - 1)
                case SB_THUMBTRACK
                    VscrollPos = Hiword(wParam)
                case SB_THUMBPOSITION
                    VscrollPos = Hiword(wParam)
            end select
            VscrollPos = max(0,min(VscrollPos,index-nDispLines+1))
            if(VscrollPos <> temp) then
                InvalidateRect(hw,null,true) ' Cause re-paint
            endif
            return
           
        ''
        '' Windows is being repainted
        ''
        'Case WM_COMMAND ' A Menu Item was selected

        Case WM_PAINT
            Dim As RECT rct
            Dim As PAINTSTRUCT ps

            GetClientRect(hw, @rct)
            nDispLines = (rct.bottom / yChar) - 1
            if((not ScrollBarEnabled) and ((index+1)>nDispLines)) then
                ScrollBarEnabled = true
                EnableScrollBar(hw,SB_VERT,ESB_ENABLE_BOTH) ' Verticle scroll bar with both arrows
                ShowScrollBar(hw,SB_VERT,true)
            endif
            '-------------------------
            if(ScrollBarEnabled) then
                si.cbSize = SizeOf(si)
                si.fMask  = SIF_RANGE or SIF_PAGE Or SIF_POS
                si.nMin   = 0
                si.nMax   = index
                si.nPage  = nDispLines
                si.nPos   = VscrollPos
                SetScrollInfo(hw,SB_VERT,@si,true)
                'print "yChar =";yChar ,"nDispLines=";nDispLines,"VscrollPos@si=";VscrollPos,"index=";index
            endif

            '===========
            'Begin Paint
            '===========
            hdc = BeginPaint(hw, @ps)
            SelectObject(hdc,GetStockObject(OEM_FIXED_FONT))
            SetBkMode(hdc,TRANSPARENT)
            'SetTextColor(hdc,&HF0F0F0)
            for i as integer = VscrollPos to min(index,VscrollPos+nDispLines-1)
                SetTextColor(hdc,colours(i))
                'print "i=";i,"colours(i)=";colours(i)
                DrawText(hdc,lines(i),-1,@rct,DT_LEFT)
                rct.top += yChar
            next i
            'print "yChar=";yChar ,"nDispLines=";nDispLines,"VscrollPos=";VscrollPos
            EndPaint(hw, @ps)
           
            return           
       
        ''
        '' Key pressed
        ''
        Case WM_KEYDOWN
            dim as integer temp = VscrollPos ' save a copy
            select case lobyte(wParam)
                case VK_ESCAPE
                    PostMessage(hw,WM_QUIT,0,0) 'Close our main window, if Esc key pressed
                case VK_UP
                    VscrollPos -= 1
                case VK_DOWN
                    VscrollPos += 1
                case VK_PRIOR
                    VscrollPos -= (nDispLines - 1)
                case VK_NEXT
                    VscrollPos += (nDispLines - 1)
                case VK_RETURN
                    index += 1
                    if(index > MAXLINES) then index = MAXLINES
                    dim as string * 64  buf
                    sprintf(buf,"This is key   line %3d,   index=%3d",index+1,index)
                    lines(index) = buf
                    colours(index) = rainbow(rotate) : rotate = (rotate + 1) mod 7
                    InvalidateRect(hw,null,true) ' Cause re-paint
            end select
            VscrollPos = max(0,min(VscrollPos,index-nDispLines+1))
            if(VscrollPos <> temp) then
                InvalidateRect(hw,null,true) ' Cause re-paint
            endif
            return

        ''
        '' User clicked the form
        Case WM_LBUTTONDOWN
            index += 1
            if(index > MAXLINES) then index = MAXLINES
            dim as string * 64  buf
            sprintf(buf,"This is mouse line %3d,   index=%3d",index+1,index)
            lines(index) = buf
            colours(index) = rainbow(rotate) : rotate = (rotate + 1) mod 7
            InvalidateRect(hw,null,true) ' Cause re-paint
            return
        ''
        '' Window was closed
        ''
        Case WM_DESTROY
            PostQuitMessage(0)
            return
    End Select
   
    ''
    '' Message doesn't concern us, send it to the default handler
    ''
    DefWindowProc(hw, message, wParam, lParam)   
   
End Sub

'' ::::::::
'' name: WinMain
'' desc: A WIN32 GUI program entry point
''
'' ::::::::
Function WinMain (ByVal hInstance As HINSTANCE, _
                   ByVal hPrevInstance As HINSTANCE, _
                   szCmdLine As String, _
                   ByVal iCmdShow As Integer) As Integer   
     
    Dim wMsg As MSG
    Dim wcls As WNDCLASS   
    Dim szAppName As String
    Dim hWnd As HWND

    ' Read an icon from shell32.dll
    Dim As HANDLE hIconLib, hDll
    hIconLib = LoadLibrary("shell32")
    wcls.hIcon = LoadIcon(hIconLib,cptr(any ptr,44))   ' get an icon
    FreeLibrary(hIconLib)

    ''
    '' Setup window class
    ''
    szAppName = "TestScroll"
     
    With wcls
        .style         = CS_HREDRAW Or CS_VREDRAW
        .lpfnWndProc   = @WndProc	' warning suspicious pointer assignment
        .cbClsExtra    = 0
        .cbWndExtra    = 0
        .hInstance     = hInstance
        ''.hIcon         = LoadIcon(NULL, IDI_APPLICATION)
        .hCursor       = LoadCursor(NULL, IDC_ARROW)
        '.hbrBackground = GetStockObject(GRAY_BRUSH)
        .hbrBackground = CreateSolidBrush(&H610000)
        .lpszMenuName  = NULL
        .lpszClassName = strptr(szAppName)
    End With
         
    ''
    '' Register the window class 
    ''   
    If(RegisterClass(@wcls) = FALSE) Then
       MessageBox(null, "Failed to register wcls!", szAppName, MB_ICONERROR)
    Else   
    ''
    '' Create the window and show it
    ''
    dim rxy as RECT   
    SystemParametersInfo(SPI_GETWORKAREA,0,@rxy,0)
    Dim vsize as integer = rxy.bottom * 0.5 ' The virtical size of the main client area

    hWnd = CreateWindowEx(0, _
                           szAppName, _
                           "Testing Scroll", _
                           WS_TILEDWINDOW, _
                           (rxy.Right - WINDOWWIDTH1)/2, _
                           (rxy.bottom - vsize)/2, _
                           WINDOWWIDTH1, _
                           vsize, _
                           NULL, _
                           NULL, _
                           hInstance, _
                           NULL)
                         

    ShowWindow(hWnd, iCmdShow)
    ShowScrollBar(Hwnd,SB_VERT,false)
    UpdateWindow(hWnd)
    ''
    '' Process windows messages
    ''
    While(GetMessage(@wMsg, NULL, 0, 0) <> FALSE)   
        TranslateMessage(@wMsg)
        DispatchMessage(@wMsg)
    Wend
    End If
    ''
    '' Program has ended
    ''
    Function = wMsg.wParam

End Function

WinMain(GetModuleHandle(null), null, Command, SW_NORMAL)
Josep Roca
Posts: 564
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

Re: Simple tutorial to create first Windows applications

Post by Josep Roca »

> 1. You declare WndProc as a function. That's possible but very unusual, as nobody will ever used the returned value

I beg to differ. What is unusual is to use a sub (it's the first time that I have seen that). The returned value is used by Windows. For example: if you want to abort the creation of the window, you have to return -1 in WM_CREATE; if you want to avoid the background to be erased, you have to return 1 in WM_ERASEBKGND; some notification messages may also require to return a value.
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Simple tutorial to create first Windows applications

Post by jj2007 »

Josep Roca wrote:> 1. You declare WndProc as a function. That's possible but very unusual, as nobody will ever used the returned value

I beg to differ. What is unusual is to use a sub (it's the first time that I have seen that). The returned value is used by Windows. For example: if you want to abort the creation of the window, you have to return -1 in WM_CREATE; if you want to avoid the background to be erased, you have to return 1 in WM_ERASEBKGND; some notification messages may also require to return a value.
You are right, and I was wrong, Josep! What confused me was this:

Code: Select all

        case WM_SIZE
            VscrollPos = max(0,min(VscrollPos,index-nDispLines+1))
            InvalidateRect(hw,null,true) ' Cause re-paint
            exit function
... where I replaced the exit function with a simple return. Now I tried return 1, and it throws a compiler error. So it's possible to return from a Sub, but not to return a value. Sounds logical, somehow - I was confused by the usage in Assembly.

What exactly does exit function then? Is it the right way to return (?) a value from a WM_* handler in FB?

Btw this works fine with Sub WndProc:

Code: Select all

        case WM_ERASEBKGND
        	Dim as handle sbrush=CreateSolidBrush(&haaaa00)
        	Dim as RECT rc
        	GetClientRect(hw, @rc)
        	FillRect(wParam, @rc, sbrush)
        	DeleteObject(sbrush)
        	return
Since DeleteObject will return 1, the WM_ERASEBKGND handler will return 1 in eax, thus signalling that the background was painted. But, as Josep rightly writes above, it is better to use Function WndProc and return 1.
Josep Roca
Posts: 564
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

Re: Simple tutorial to create first Windows applications

Post by Josep Roca »

> What exactly does exit function then? Is it the right way to return (?) a value from a WM_* handler in FB?

Return is a shortcut for FUNCTION = value : EXIT FUNCTION. If you simply use EXIT FUNCTION, it is the same that if you did FUNCTION = 0 : EXIT FUNCTION, that is, FB returns a value of 0, and it is the same that if you simply use RETURN. I use FUNCTION = value instead of RETURN <value> sometimes out of habit, because PowerBasic does not implement RETURN.
Macq
Posts: 24
Joined: Feb 18, 2021 4:01
Location: Queensland, Australia

Re: Simple tutorial to create first Windows applications

Post by Macq »

@jj2007
I don't get any warnings when I compile my original code as 32bit or 64bit. The sprintf() works fine.

G:\WinFBE_Suite\FreeBASIC-1.07.1-gcc-8.4> bin\win32\gcc.exe --version
gcc.exe (MinGW-W64 i686-posix-dwarf, built by Brecht Sanders) 8.4.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

G:\WinFBE_Suite\FreeBASIC-1.07.1-gcc-8.4> bin\win64\gcc.exe --version
gcc.exe (MinGW-W64 x86_64-posix-dwarf, built by Brecht Sanders) 8.4.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Simple tutorial to create first Windows applications

Post by jj2007 »

Josep Roca wrote:If you simply use EXIT FUNCTION, it is the same that if you did FUNCTION = 0 : EXIT FUNCTION, that is, FB returns a value of 0, and it is the same that if you simply use RETURN.
Correct. I had a closer look: FB sets "function" alias local.1 to zero on entry to WndProc. Exit function is simply a jmp to the end of the WndProc, where mov eax, local.1 ensures that zero is returned:

Code: Select all

00401590  /.  55            push ebp		; WndProc entry
00401591  |.  89E5          mov ebp, esp
00401593  |.  83EC 64       sub esp, 64
00401596  |.  53            push ebx
00401597  |.  56            push esi
00401598  |.  57            push edi
00401599  |.  C745 FC 00000 mov dword ptr [local.1], 0	; function = 0
...
        	GetClientRect(hw, @rc)
        	FillRect(wParam, @rc, sbrush)
        	DeleteObject(sbrush)	' returns 1...
        	asm int 3
        	Exit function
...
00401655  |.  8D45 E4       lea eax, [local.7]
00401658  |.  50            push eax                                 ; /Rect => offset LOCAL.7
00401659  |.  FF75 08       push dword ptr [arg.1]                   ; |hWnd => [ARG.1]
0040165C  |.  E8 AF0A0000   call <jmp.&USER32.GetClientRect>         ; \USER32.GetClientRect
00401661  |.  FF75 F4       push dword ptr [local.3]                 ; /hBrush => [LOCAL.3]
00401664  |.  8D45 E4       lea eax, [local.7]                       ; |
00401667  |.  50            push eax                                 ; |Rect => offset LOCAL.7
00401668  |.  FF75 10       push dword ptr [arg.3]                   ; |hDC => [ARG.3]
0040166B  |.  E8 A80A0000   call <jmp.&USER32.FillRect>              ; \USER32.FillRect
00401670  |.  FF75 F4       push dword ptr [local.3]                 ; /hObject => [LOCAL.3]
00401673  |.  E8 080A0000   call <jmp.&GDI32.DeleteObject>           ; \GDI32.DeleteObject
00401678  |.  CC            int3
00401679  |.  E9 34070000   jmp 00401DB2
...
00401DB2  |> \8B45 FC       mov eax, [ebp-4]    ; <<<<<<<<<<<<< return in eax Local.1 alias "function"
00401DB5  |.  5F            pop edi
00401DB6  |.  5E            pop esi
00401DB7  |.  5B            pop ebx
00401DB8  |.  89EC          mov esp, ebp
00401DBA  |.  5D            pop ebp
00401DBB  \.  C2 1000       retn 10
In contrast, a return n moves n into "function", i.e. local.1, and then jmps to 00401DB2 where eax gets loaded from local.1 alias [ebp-4].
Post Reply