View Full Version : thinBasic Help
I was doing a test tonight to see if I can send my own custom message via the messaging system to another control.
I have no idea how to set the msg, wparam and lparam, so this program sort of records those too to help figure out, but I am stuck.
The idea is you click on the SendMessage button, it should then send a message to the system and hopefuly the message handling will see it and then move the second button the reciever to the top right corner. THere is a listbox that shows the msg wparam and lparam each time the button is pushed.
You can also see the commented lines of all my tries of things I tried. Hopefully it will be possible to make this happen :)
'---12-12-2006
USES "UI"
' *******************************************************************
' * CONSTANTS *
' *******************************************************************
'---Form ID's
%MessageTest = 1
'---Control ID's
%Sendmessage = 2
%Reciever = 3
'---These need to be added to thinBasic Constants
%SBS_RIGHTALIGN = &H4& 'scrollbar style
%SBS_VERT = &H1& 'scrollbar style
%LVS_LIST = &H3& 'list view style
%LVS_REPORT = &H1& 'list view style
%LVS_EX_GRIDLINES = &H1& 'extended list view style
%MCS_WEEKNUMBERS = &H4& 'calendar style
' *******************************************************************
' * TYPES *
' *******************************************************************
' *******************************************************************
' * GLOBAL VARIABLES *
' *******************************************************************
'General Vars
GLOBAL hDlg AS LONG
GLOBAL msg, wParam,lParam as long
GLOBAL m$ as string = " "
' *******************************************************************
' * MAIN PROGRAM *
' *******************************************************************
DIALOG NEW PIXELS , 0, "Message Test", 310, 0, 357, 483, %WS_SYSMENU, %MessageTest TO hDlg
DIALOG SHOW modeless hDlg
'------------------------------Program Loop
while isWindow(hDlg)
msg = GetMessage(hDlg, wParam, lParam)
m$ = "msg: "+Str$(msg)+" wparam: "+Str$(wParam)+" Lparam: "+Str$(lparam)
DIALOG SET TEXT hDlg, m$
'LISTBOX ADD hDlg, 4, m$
select case msg
case %WM_INITDIALOG
CONTROL ADD BUTTON , hDlg, %Sendmessage, "SendMessage", 3, 447, 80, 30
CONTROL ADD BUTTON , hDlg, %Reciever, "Reciever", 87, 447, 64, 24
CONTROL ADD LISTBOX , hDlg, 4, , 150, 4, 200, 488
CASE %WM_COMMAND
SELECT CASE wParam
CASE %Sendmessage
'---SendMessage Code Here
'CONTROL SEND hDlg, %Sendmessage, 29, 999999, 999999 'Not sure how we determine what we can use
CONTROL SEND hDlg, %Reciever, 29, 999999, 999999
'CONTROL SET LOC hDlg, %Reciever, 10, 10 'This works
'Msg = GetMessage(hDlg, wParam, lParam)
Msg = PeekMessage(hDlg, wParam, lParam)
m$ = "msg: "+Str$(msg)+" wparam: "+Str$(wParam)+" Lparam: "+Str$(lparam)
LISTBOX ADD hDlg, 4, m$
CASE %Reciever
'---Reciever Code Here
END SELECT
CASE 29
SELECT CASE wParam
CASE 999999
CONTROL SET LOC hDlg, %Reciever, 10, 10
END SELECT
CASE %WM_SYSCOMMAND
SELECT CASE wParam
CASE %SC_CLOSE
EXIT WHILE
END SELECT
END SELECT
wend
'------------------------------Program Clean Up
DIALOG END %MessageTest
' *******************************************************************
' * FUNCTIONS *
' *******************************************************************
Went to try to sleep but thought of this so got back up to write the question, then I can sleep soundly :)
Type tObject
name as string
x as integer
y as integer
width as integer
height as integer
End Type
In the above type, the variable names are obviously stored somewhere, can we access those names from an array in our programs.
This would eliminate having to type those by hand elsewhere and eliminate 2 or more places to keep making changes when you change the type field's name. If we could access that array, just change the name of the field in the type definition and it will be updated in the rest of your code. Thanks will be interesting to see if possible.
If I had a procedure that just belonged to that object, in oop this is how it is done, but to mimic it in thinBasic, is there a way to point to that procedure from the type definition, so it could be called with the sybtax object.callFunction()
callFunction would be a field in the type but if there was a way to access it this way it would be neat. I am sure something to do with Pointers, but not sure how all that works in thinBasic.
Thanks, but all of this has been on my mind and I am sort of ready to test out these concepts in thinBasic.
ErosOlmi
13-12-2006, 15:16
Kryton9,
regarding first request to send messages ...
In thinBasic, not all possible window messages are handled. They are too many and would slow down too much WHILE/WEND message pump.
Anyhow, I've changed UI module to to accept handling message above %WM_USER using DIALOG SEND ...
So you will be able to send a message like:
...
CASE %WM_COMMAND
SELECT CASE wParam
CASE %Sendmessage
'---SendMessage Code Here
dialog SEND hDlg, %wm_user + 1, %Reciever, 999999
CASE %Reciever
END SELECT
CASE %wm_user + 1
select case wParam
case %Reciever
select case lParam
case 999999
CONTROL SET LOC hDlg, %Reciever, 10, 10
end select
end select
...
Please wait for next update. I need to fix some other points (about 1 week or so).
Regarding your second post: not sure to have understood correctly what you mean but I will try to reply.
Unfotunately dynamic strings declarations (example: Name AS STRING) cannot be inside UDT elements. This is because UDT must be sized to a well known size and dynamic strings are dynamic for nature so their size cannot be predetermined. But you can do Name AS STRING * 64 inside UDT.
UDT names, again, must be known at pre-parsing stage because thinBasic has to store some internal data structures to be able to quickly access script info. So executing tObject.Name where Name is a reference of a element name somewhere else stored is something thinBasic cannot do.
But functions can be executed dynamically using CALL statement without knowing function name at script time. CALL works in this way:
when CALL is encountered, parser analyze next token;
if token is a function name, it is executed directly;
if token is a variable or a constant or a quoted string, a string expression is evaluated and result is considered a function name to be executed
So
DIM Count AS LONG = 123
CALL "myFunctionNumber" & Count ("[whatever parameter list]")
is a valid thinBasic statement and MyFunctionNumber123 will be executed (if present in the script).
Last, OOP and all that works around OOP world is something we are not still ready to figure out in thinBasic.
Eros
Eros, that is really great news about the messaging system. This way we can have triggers in our engine that can send out a message when something happens or an player object could be injured and ask for help etc. It makes it very interesting now!!
About my second question, I think my example confused the issue, you did a great job explaining many things. Thanks.
I was trying to figure out this...
My Dad called, I have to run to do some errands. I will write tonight when I get back and reply to the other posts, so sorry guys if I am a little late replying today.
Ok back to the second question :)
When a type is defined, the field names must be stored somewhere in memory by thinBasic. Is it possible to get those names back, to read them from memory maybe with a series of peek commands or just direct access as an array?
The reason is, imagine the following:
Type tObject
Field1Name
Field2Name
Field3Name
Field4Name
Field5Name
End Type
Those names and whatever type they are stored somewhere.
Now if I were making a form to display information about the type I would manually have to create the label controls with the text/caption to match the names in the fieldnames. But If I could access the stored names in memory, I could just write a simple loop to generate the label controls and to assing the text/caption from the names provided by the type definition as stored in memory.
If I wanted to make another form to change properties in a type defined object, I could again use this same information.
So if any changes to the name of the field in the original type definition would be updated automatically by the other code since it is reading those names from memory.
I hope I was able to explain it better this time. Thanks.
Petr Schreiber
14-12-2006, 23:53
Hi,
so something like dynamic function names just with variables ?
Thanks,
Petr
Not only function names but to read the field names from the type definition and not have to code them in the rest of the program.
I am going to do some console tests tonight to see if I can make an example of what I am thinking about. Don't know how far I will get but will try :)
Here is a test I did, got something back but not what I expected :) At the end of the program, commented you will see what I was looking for and what I am asking about, thanks for any insights!!
uses "console"
type tobject
name as asciiz *255
address as asciiz * 255
city as asciiz * 255
state as asciiz * 255
zip as integer
end type
dim obj as tobject
obj.name = "mr. data processor"
obj.address = "123 main street"
obj.city = "las vegas"
obj.state = "nevada"
obj.zip = 89120
dim s as asciiz * 4048
dim pr as long = VARPTR(obj)
s = PEEK$(Pr, 4048)
Console_Write(format$(pr))
Console_Write(s)
' where can I peek$ to see the field names: name, address,city, state and zip?
' I am interested in the names not the values for what I have in mind :)
Ok, back after a while after I posted this. I decided to do some reading based on what Eros wrote earlier. I have heard token mentioned many times, but never really knew what it was, after further reading... I see that the interpreter breaks code up into 5 token groups, so if I understand correctly, the fields names are not stored as names, but are some sort of token, which I am guessing is something in hexadecimal maybe? But not stored as a string in memory as I was thinking. Is this correct? So doing what I wanted, does not seem doable.
ErosOlmi
15-12-2006, 14:38
Kryton9,
what you did is correct but the matter is inside CONSOLE_WRITE and in general all windows way to output strings: you cannot output NULL chars. Also standard Win32 API MessageBox cannot output NULL chars because NULL is considered end of string in ANSI strings. You can get some more fortune using CONSOLE_WRITELINE because it breaks the string you want to output by pieces long as current console width.
When you assign a string value to an ASCIIZ string, a NULL char is inserted at the end of the string to maintain compatibility with ANSI strings. If you output you string buffer in a file and open that file with a hex editor you will see your complete structure. Remember that numbers are in binary format.
Also consider those few thinBasic specific points not mentioned in help:
pr = VARPTR(obj)
s = PEEK$(Pr, 4048)
is equivalent at
s = obj
This because when you use an UDT without any member name, thinBasic consider it as a binary string.
So something like
DIM a AS obj
DIM b AS obj
DIM s AS STRING
a = b
s = a
b = s
are all valid thinBasic statements.
Another one is the size of a variable. You can use SIZEOF function that will return the size of UDT structure.
So your code
s = PEEK$(Pr, 4048)
can be changed into
s = PEEK$(Pr, SIZEOF(obj))
or
s = PEEK$(Pr, SIZEOF(tObject))
In this way code is dynamic and changes in tObject structure will not effect your code.
So your code can be something like:
uses "console"
uses "file"
type tObject
szName as asciiz * 255
szAddress as asciiz * 255
szCity as asciiz * 255
szState as asciiz * 255
szZip as integer
end type
dim obj as tObject
obj.szName = "mr. data processor"
obj.szAddress = "123 main street"
obj.szCity = "las vegas"
obj.szState = "nevada"
obj.szZip = 89120
Console_Writeline(obj)
file_save( app_sourcepath & "__OBJOUT.bin", obj )
console_waitkey
Ciao
Eros
ErosOlmi
15-12-2006, 15:15
Now on tokens. Please do not take this as general way of doing interpreting but just how thinBasic does it. There are many theories on tokenizing, parsing, interpreting. We just follow our personal one reading and learning all around what other clever people did before us.
Token is a general word that can means many different things.
Here we consider token a single sequence of chars that cannot be divided into a smaller piece to be analyzed.
One of the very first steps thinBasic engine performs is a tokenizer process, that is the process to break your script in all the "indivisible parts" (read tokens) found inside it. At the end of tokenizer process, every single token has no meaning at all. Tokens are just sequence of chars. So now we need to understand if every token has a meaning and which.
Here it starts the optimization process. This process must analyze every token and try to understand token type: delimiter, number, quoted string, keywords, undefined. At the same time an optimizer stores some internal data structures needed to classify already resolved tokens once and for all.
Now it starts the real parsing plus again optimization process. Script start being analyzed again but now also execution takes place. Undefined tokens are here resolved into: global variables, local variables, functions name, parameters name, … If something is not resolved at this stage, a run-time error is generated.
Regarding your idea of using some king of naming reference. The idea is good and can be considered for interesting development. It is like using indirect cells reference in Excel. It is not something that cannot be done because usually it is not done. The problem is again priority and time. I will add a note in my to-do list at least as something to re-think. I like the idea to add more dynamic parsing in thinBasic, like we did in CALL statement.
So thanks for "dreaming" of it :D
Ciao
Eros
Eros, wow, thanks so much for that great example and insight into the magic behind the scenes with thinBasic and how this is done. That was a very very nice and clear explanation. It makes me appreciate the incredible job you are doing in making thinBasic do the wonders it does.
Thanks for the example too, it just shows how well you designed thinBasic that it already handles UDT's in such a great way and that we can have those kind of assignments.
Last night while I slept, really with the power of having global variables, global UDT's, the function call feature, and the new messaging system, we can have a very nice OOP like programming features but with the nice clean Basic syntax we all love.
I did a test before going to sleep, I had done one before, but I forgot the results and couldn't find the posts I made about it, but anyways instead of passing a UDT that is assigned to an array as myFunction (myUDTArrray(x)) you can just send myFunction (x), since the UDT array is global this works just as well.
Now if the function were to handle another arrayUDT of the same object type, I am not sure how we can do that.
So:
Type tObject
x as integer = 10
y as integer = 10
End Type
GLOBAL myUDTArrayOne(10) as tObject
GLOBAL myUDTArrayTwo(10) as tObject
'Program Loop
local x as integer
for x = 1 to 10
myFunction (myUDTArrayOne(x) )
myFunction (myUDTArrayTwo(x) )
next
'Program End
Function myFunction (UDTArray as tObject)
UDTArray.x += 2
UDTArray.y += 1
End Function
ErosOlmi
15-12-2006, 20:43
Kryton9,
I have to check your example.
It was supposed to work but is not. I will get you back ASAP.
Eros
Well if we get that working, then there is nothing to stop us, that will be awesome, every day I learn a little more about thinBasic and am always that much more impressed!! Thanks for such a great language, fast and fun, what a combo and I should add with growing power!!