Bill Aitken's Tutorial - Procedure Frame

Started by BPak, April 14, 2008, 01:36:42 AM

Previous topic - Next topic

BPak

Hi

I have been working through Bill's great tutorial on using EasyCode with GoASM and have come upon a problem in the Third section PROCEDURE where the Frame is introduced.

The program works fine until I go to use the LOCAL VARIABLES and then plays up.

EasyCode compiles it ok but when it is run the Exit Button is not displayed at all on the Window.

I have experimented a bit with it to try finding where I have made an error.

Changed each of the Local Variables back to the DATA section one by one with no success - unless ALL Variables were in the DATA section.

The tried putting them back as LOCAL and hard wiring the pegBorder to Mov Eax, 4 instead of Mov Eax, [pegBorder] and IT WORKED!

Beats me why the pegObject can find all the Variables when they are in the DATA Section and not find the pegBorder when hButton and pBox ar LOCAL Vars. ???

I read carefully about FRAME which says when the prog is IN the Frame it cannot communicate with Vars outside and when not in the FRAME outside code can not see in the FRAME.

When all the Variables are in DATA it appears the code in the FRAME can see those variables.

Below is the code: (I have added some other code to try out by using Local var for pegBorder in the OnResize procedure and passing the pegBorder by parameter) which does work.

.Const

.Data

MESSAGES DD WM_CREATE, OnCreate
DD WM_CLOSE, OnClose
DD WM_SIZE, OnResize

;hButton HWND
;pBox RECT
;pegBorder DB 4

.Code

winMainProcedure Frame hWnd, uMsg, wParam, lParam
Mov Eax, [uMsg]
Mov Ecx, SizeOf MESSAGES / 8
Mov Edx, Addr MESSAGES
: Dec Ecx
Js > L2
Cmp [Edx + Ecx * 8], Eax
Jne <
Call [Edx + Ecx * 8 + 4]
Ret
L2: Return (FALSE)
EndF

OnCreate:
UseData winMainProcedure
;==================================
;Write the initialization code here
;==================================
Return (TRUE)
EndU

OnClose:
UseData winMainProcedure
;=========================
;Write the final code here
;=========================
Invoke IsModal, [hWnd]
Or Eax, Eax ;Cmp Eax, FALSE
Jz >
Invoke EndModal, [hWnd], IDCANCEL
Mov Eax, TRUE ;Return (TRUE)
: Ret
EndU

winMainbtnExit Frame hWnd, uMsg, wParam, lParam
Return (FALSE)
EndF
OnResize:
UseData winMainProcedure
Local pegBord:D
Mov D[pegBord], 8
; Invoke pegObject, [hWnd], IDC_WINMAIN_BTNOK, [pegBord]
Invoke pegObject, [hWnd], IDC_WINMAIN_BTNEXIT, [pegBord]
Return (TRUE)
EndU

pegObject Frame hWnd, idControl, pegBorder
Local hButton, pBox:RECT

; 1. get handle on button
Invoke GetWindowItem, [hWnd], [idControl]
Mov [hButton], Eax
; 2. Get new size of windows client area
Invoke GetClientRect, [hWnd], Addr pBox
; 3. Get width of button and subtract from with of client area
;  and border
Invoke GetWidth, [hButton]
Add Eax, [pegBorder]
Mov Ebx, [pBox.right]
Sub Ebx, Eax
; 4. set left pisition of button
Invoke SetLeft, [hButton], Ebx

; 5. work top as above
Invoke GetHeight, [hButton]
Add Eax, [pegBorder]
Mov Ebx, [pBox.bottom]
Sub Ebx, Eax
; 6. set top of button
Invoke SetTop, [hButton], Ebx

Return (TRUE)
EndF

winMainbtnOK Frame hWnd, uMsg, wParam, lParam
Return (FALSE)
EndF



stevenp

Frames can see variables in the Data section because those have global scope.

I tried your code as is and it worked for me.  I then changed to the following real quick and it also worked.  Can you help me understand more precisely what isn't working for you?  I'd really like to help.

.Const

.Data

MESSAGES DD WM_CREATE, OnCreate
DD WM_CLOSE, OnClose
DD WM_SIZE, OnResize

;hButton HWND
;pBox RECT
;pegBorder DB 4

.Code

winMainProcedure Frame hWnd, uMsg, wParam, lParam
Mov Eax, [uMsg]
Mov Ecx, SizeOf MESSAGES / 8
Mov Edx, Addr MESSAGES
: Dec Ecx
Js > L2
Cmp [Edx + Ecx * 8], Eax
Jne <
Call [Edx + Ecx * 8 + 4]
Ret
L2: Return (FALSE)
EndF

OnCreate:
UseData winMainProcedure
;==================================
;Write the initialization code here
;==================================
Return (TRUE)
EndU

OnClose:
UseData winMainProcedure
;=========================
;Write the final code here
;=========================
Invoke IsModal, [hWnd]
Or Eax, Eax ;Cmp Eax, FALSE
Jz >
Invoke EndModal, [hWnd], IDCANCEL
Mov Eax, TRUE ;Return (TRUE)
: Ret
EndU

winMainbtnExit Frame hWnd, uMsg, wParam, lParam
Return (FALSE)
EndF
OnResize:
UseData winMainProcedure
; Invoke pegObject, [hWnd], IDC_WINMAIN_BTNOK, [pegBord]
Invoke pegObject, [hWnd], IDC_WINMAIN_BTNEXIT ;, [pegBorder]
Return (TRUE)
EndU

pegObject Frame hWnd, idControl ;, pegBorder ***<- commented out for test***
Local hButton, pBox:RECT
Local pegBorder:D ; ***<- added this as local***
Mov D[pegBorder], 8

; 1. get handle on button
Invoke GetWindowItem, [hWnd], [idControl]
Mov [hButton], Eax
; 2. Get new size of windows client area
Invoke GetClientRect, [hWnd], Addr pBox
; 3. Get width of button and subtract from with of client area
;  and border
Invoke GetWidth, [hButton]
Add Eax, [pegBorder]
Mov Ebx, [pBox.right]
Sub Ebx, Eax
; 4. set left pisition of button
Invoke SetLeft, [hButton], Ebx

; 5. work top as above
Invoke GetHeight, [hButton]
Add Eax, [pegBorder]
Mov Ebx, [pBox.bottom]
Sub Ebx, Eax
; 6. set top of button
Invoke SetTop, [hButton], Ebx

Return (TRUE)
EndF

BPak

QuoteFrames can see variables in the Data section because those have global scope.

I tried your code as is and it worked for me.  I then changed to the following real quick and it also worked.  Can you help me understand more precisely what isn't working for you?  I'd really like to help.

OK.

Following the tutorial through you end up with code as follows and it does not appear to get to the Variable pegBorder.

pegBoirder is STILL in the DATA Section.

hButton and pBox are LOCAL

The prog will compile but when you run it the BUTTON is not on the Window. It has disappeared.

If you hard wire pegBorder in the pegObject to say 8 then it works. To me that says pegBorder has not been accessible in the DATA Section.


.Const

.Data

MESSAGES DD WM_CREATE, OnCreate
DD WM_CLOSE, OnClose
DD WM_SIZE, OnResize

;hButton HWND
;pBox RECT
pegBorder DB 4

.Code

winMainProcedure Frame hWnd, uMsg, wParam, lParam
Mov Eax, [uMsg]
Mov Ecx, SizeOf MESSAGES / 8
Mov Edx, Addr MESSAGES
: Dec Ecx
Js > L2
Cmp [Edx + Ecx * 8], Eax
Jne <
Call [Edx + Ecx * 8 + 4]
Ret
L2: Return (FALSE)
EndF

OnCreate:
UseData winMainProcedure
;==================================
;Write the initialization code here
;==================================
Return (TRUE)
EndU

OnClose:
UseData winMainProcedure
;=========================
;Write the final code here
;=========================
Invoke IsModal, [hWnd]
Or Eax, Eax ;Cmp Eax, FALSE
Jz >
Invoke EndModal, [hWnd], IDCANCEL
Mov Eax, TRUE ;Return (TRUE)
: Ret
EndU

winMainbtnExit Frame hWnd, uMsg, wParam, lParam
Return (FALSE)
EndF
OnResize:
UseData winMainProcedure
; Invoke pegObject, [hWnd], IDC_WINMAIN_BTNOK
Invoke pegObject, [hWnd], IDC_WINMAIN_BTNEXIT
Return (TRUE)
EndU

pegObject Frame hWnd, idControl
Local hButton, pBox:RECT

; 1. get handle on button
Invoke GetWindowItem, [hWnd], [idControl]
Mov [hButton], Eax
; 2. Get new size of windows client area
Invoke GetClientRect, [hWnd], Addr pBox
; 3. Get width of button and subtract from with of client area
;  and border
Invoke GetWidth, [hButton]
Add Eax, [pegBorder]
Mov Ebx, [pBox.right]
Sub Ebx, Eax
; 4. set left pisition of button
Invoke SetLeft, [hButton], Ebx

; 5. work top as above
Invoke GetHeight, [hButton]
Add Eax, [pegBorder]
Mov Ebx, [pBox.bottom]
Sub Ebx, Eax
; 6. set top of button
Invoke SetTop, [hButton], Ebx

Return (TRUE)
EndF

winMainbtnOK Frame hWnd, uMsg, wParam, lParam
Return (FALSE)
EndF

BPak

Think I may have found the solution.

In the DATA Section

pegBorder DB 4

change to

pegBorder DD 4

Now save, re-build, run and it displays both buttons as expected.
---------------------------------------------------------------------------------------

At first I tried

Add Eax, B[pegBorder]

in the pegObject proc to show it was a DB being added to EAX and this displayed an error.

So the solution was to change the pegBorder to a  DD   ??




stevenp

Just for the heck of it I tried getting it to work with a Byte.  Obviously, it should be a DD (DWORD) because a byte is not big enough.  You'll see it compiles but that it only works when the form is small:

BTW, how do I move the global variable "clientDimensions DB 11 Dup 0" as a local variable under the Frame displayWindowSize?


.Const

.Data

MESSAGES DD WM_CREATE, OnCreate
DD WM_CLOSE, OnClose
DD WM_SIZE, OnResize

clientDimensions DB 11 Dup 0 ; <-- How do I move this as a local variable under the Frame displayWindowSize

;hButton HWND
;pBox RECT
pegBorder DB 4

.Code

winMainProcedure Frame hWnd, uMsg, wParam, lParam
Mov Eax, [uMsg]
Mov Ecx, SizeOf MESSAGES / 8
Mov Edx, Addr MESSAGES
: Dec Ecx
Js > L2
Cmp [Edx + Ecx * 8], Eax
Jne <
Call [Edx + Ecx * 8 + 4]
Ret
L2: Return (FALSE)
EndF

OnCreate:
UseData winMainProcedure
;==================================
;Write the initialization code here
;==================================
Return (TRUE)
EndU

OnClose:
UseData winMainProcedure
;=========================
;Write the final code here
;=========================
Invoke IsModal, [hWnd]
Or Eax, Eax ;Cmp Eax, FALSE
Jz >
Invoke EndModal, [hWnd], IDCANCEL
Mov Eax, TRUE ;Return (TRUE)
: Ret
EndU

winMainbtnExit Frame hWnd, uMsg, wParam, lParam
Return (FALSE)
EndF
OnResize:
UseData winMainProcedure

; Invoke pegObject, [hWnd], IDC_WINMAIN_BTNOK, [pegBord]
Invoke pegObject, [hWnd], IDC_WINMAIN_BTNEXIT, [pegBorder]
Invoke displayWindowSize, [hWnd]

Return (TRUE)
EndU

pegObject Frame hWnd, idControl, pegBorder
Local hButton, pBox:RECT
;Local pegBorder:D
;Mov D[pegBorder], 8

; 1. get handle on button
Invoke GetWindowItem, [hWnd], [idControl]
Mov [hButton], Eax
; 2. Get new size of windows client area
Invoke GetClientRect, [hWnd], Addr pBox
; 3. Get width of button and subtract from with of client area
;  and border
Invoke GetWidth, [hButton]
Add Al, [pegBorder]
Mov Bl, [pBox.right]
Sub Bl, Al
; 4. set left pisition of button
Invoke SetLeft, [hButton], Ebx

; 5. work top as above
Invoke GetHeight, [hButton]
Add Al, [pegBorder]
Mov Bl, [pBox.bottom]
Sub Bl, Al
; 6. set top of button
Invoke SetTop, [hButton], Ebx

Return (TRUE)
EndF

displayWindowSize Frame hWnd

Local clientRect:RECT

; Call API to get client area for window
Invoke GetClientRect, [hWnd], Addr clientRect

; Set right value in client area to string.
Invoke String, [clientRect.right], Addr clientDimensions, ecDecimal

; Move the comma and space into Ebx register
Mov Ebx, ", "

; Move comma in space (in Ebx) to location starting at address @clientDimensions + contents in Eax (size of string)
Mov [clientDimensions + Eax], Ebx

; Move the new size plus two into the Eax register (move to location over.)
Add Eax, Addr clientDimensions + 2

; Convert the bottom value into a string move it into the location now in Eax
Invoke String, [clientRect.bottom], Eax, ecDecimal

; Now we set the text with the full contents in clientDimensions (NULL terminated.)
Invoke SetText, [hWnd], Addr clientDimensions

Ret
EndF

stevenp

BTW, isn't this Easy Code awesome! 

I am really glad someone else is trying to learn this at the same time as me. :dance:

BPak

Hi Steven

Yes it is awsome.

I think we have the trilogy here in
GoASm
EasyCode
Bill Aitken. (Tutorials need to be clear and this one certainly is)

If ever there was an opportunity to start out in ASM (seriously) I think this is it!!

I suppose the clientDimensions declared Locally might be found in the Go Manual if not in EasyCode. Will see if it is there. Or maybe in one of the examples.

Ramon Sala

Hi,

After talking to Bill Aitken, he asked me to change the pegBorder variable to be defined as DD. Even though it sometimes worked defined as DB, it definitely has to be a 4-byte value, that is a DD, in order to work properly. Congratulations BPak!

So, the pegBorder varaible has already been corrected in the tutorial.

Thanks,

Ramon

Greetings from Catalonia

BPak

QuoteBTW, how do I move the global variable "clientDimensions DB 11 Dup 0"  as a local variable under the Frame displayWindowSize?

I found an example of declaring the string as a Local variable in the GoAsm Help file.

Look under the FRAMES section.

; goAsm manual - Frame - the Basics (LOCALS hDC,BUFFER[256]:B)
Local clientDimensions[11]:B


Dont know if I have the whole routine correct, but it works.


displayWindowSize Frame hWnd
Local clientRect:RECT
; goAsm manual - Frame - the Basics (LOCALS hDC,BUFFER[256]:B)
Local clientDimensions[11]:B

; Call API to get client area for window
Invoke GetClientRect, [hWnd], Addr clientRect

; Set right value in client area to string.
Invoke String, [clientRect.right], Addr clientDimensions, ecDecimal

; Move the comma and space into Ebx register
Mov Ebx, ", "

; Move comma in space (in Ebx) to location starting at address @clientDimensions + contents in Eax (size of string)
Mov [clientDimensions + Eax], Ebx

Mov Ecx, Addr clientDimensions
Add Eax, Ecx
Add Eax, 2
; Convert the bottom value into a string move it into the location now in Ecx
Invoke String, [clientRect.bottom], Eax, ecDecimal

; Now we set the text with the full contents in clientDimensions (NULL terminated.)
Invoke SetText, [hWnd], Addr clientDimensions

Return (TRUE)
EndF


BPak

BPak

Hi Ramon,

Thanks for changing the pegBorder to DD.

The first time I wrote the code for the project (pegBorder DB 4) it DID Work. But I had problems with the code NOT working until I used the LOCAL variables for hButton and pBox.

So second go at it found everything worked fine UNTIL I put the hButton and pBox into the LOCAL Variables.

I checked the files against each other and found no difference.

The GoAsm help has a section about different size variables which led me to try changing the pegBorder to DD.

BPak





Ramon Sala

Hi BPak,

As Eax is a 32-bit register (4 bytes), the Add Eax, [pegBorder] sentence adds a 4-byte value to Eax (that specified by the pegBorder variable). If we define pegBorder as DB, the Addition takes the defined byte and the following three bytes, whose value is unknown, and the results are unpredictable. If we are lucky and those three bytes are set to zero, the program works fine. But if not...

So, no doubt! The pegBorder variable has to be defined as DD.


Ramon
Greetings from Catalonia

stevenp

Quote from: BPak on April 16, 2008, 03:24:36 AM
QuoteBTW, how do I move the global variable "clientDimensions DB 11 Dup 0"  as a local variable under the Frame displayWindowSize?

I found an example of declaring the string as a Local variable in the GoAsm Help file.

Look under the FRAMES section.

; goAsm manual - Frame - the Basics (LOCALS hDC,BUFFER[256]:B)
Local clientDimensions[11]:B


Dont know if I have the whole routine correct, but it works.


Thanks for looking this up.  I will play around with that when I get a chance.

Ramon Sala

Right stevenp, that is the way!

Sorry BPak, I forgot to answer that question.

Ramon
Greetings from Catalonia

cran0g

BPak,

You are perfectly right.  It should be a DD and not a DB.  I am an idiot and have no more right to God's clean air than a weasel.  However, I have no one right now to do peer reviews of the stuff and these sort of mistakes are bound to creep in.  The main thing is to let me know, as you have done.  The tutorial has been amended, courtesy of Ramon.  By the way, I can publish these tutorials as web pages (the form they are currently in) but I could also bind them up into a single exe file.  That way you'd be able to view them even when off-line.  Any preferences?

Bill

BPak

Hi Bill

I know less about Assembly than most so being a learner I have to dig deep to understand.
Your tutorials are excellent for me. I can see that you had pegBorder as a DB to explain the 127 Signed Byte with the unsigned byte of 255 issue. Those minor points get past us all sometimes!

If I find any other things I will pass it on.

I definitely would like the tutorials offline if possible.

Thank you, Allan