Windows executable programs run as a sub window in the master window of the Windows Graphical User Interface.
We call these type programs GUI programs.
In order to understand how Ebasic handles GUI programs we need to understand how the Windows OS handles GUI.
In simplest terms, Windows is made up of objects called windows. This includes what we perceive as a "normal window"
and more specialized, dedicated purpose, windows that we call "controls". The connection between a window and
the controls on the window is referred to as a parent-child relationship. A control is a child of the parent window.
Next comes the dynamic part of Windows. Communication with a particular window/control is accomplished
with what are referred to as "messages". Messages are sent and received by any given object; and the OS itself.
Messages are sent to individual windows/controls. Therefore, they always contain the object's identifier (handle), the message id,
and any data, as needed. A child may also pass a message through to its parent. Messages
are sent when something changes. These changes are referred to as "events".
The most common events that a user is aware of include moving the mouse, clicking a button, selecting an item
in a list, and entering text.
There are many, many more internal events that cause messages to be sent.
So, the Windows GUI is an event driven, message based system. If there are all these messages
being sent there has to be a way to process them. Since messages can arrive at anytime from anywhere, they have
to be saved until they can be processed. The area where they are stored is referred to as a "queue".
Once a message is processed it is removed from the queue and discarded. The section of code that 'handles' this
processing is called a "message handler". Every window/control has a default message handler that takes care
of the basic operations of that object like the actual displaying of the object, the positioning/resizing of a window, the
dropdown of a menu or combo box, etc. These default handlers keep the user from having to reinvent the wheel for every
When you click on a button the button appears to be depressed. The button's default message handler takes care of
processing the button down message and properly redraws the button. When the button is released another message
is processed and the button is redrawn in its normal appearance.
But that is only part of the picture when a button is clicked. The default message handler for the window/control
has no idea of what the window/control is being used for in a specific application. There has to be a way to
respond to the button click message (that is redrawing the button) and have it do something specific to each application.
This is where the beauty of Ebasic comes in to play.
Ebasic simplifies the way windows and controls are created by hiding all the cryptic, mundane task from the user.
Each window that is created in Ebasic has a message handler subroutine associated with it. The handler
allows the user to pick and choose which messages they want to add application specific code to.
The Ebasic scheme is very intuitive and easy to understand.
Note: The preceding, overly simplified discussion of how Windows functions is not intended to replace the detailed
discussions in the Windows SDK.
Now let's apply the above explanation to a real example.
Lets create a program with a single, fixed-size window with one control.
This program is based on one I found on the forum.
The user can click the one button, press the 'ESC' key, or click the 'x' in the upper right corner of the window to exit the program.
The Test Program:
Every program must have a starting point.
We start with definitions. Before we use a variable in a program we have to define what
type of variable it is. In this case we have a 'window' and two 'int'.
These are Ebasic types. A 'window' variable is a UDT with many members that hold information about a window.
An 'int' is an integer; a whole number.
The variable names (Main_Win, Main_Win_Style, and Main_Win_Flag) are purely arbitrary and chosen my the user
following the guidelines for naming variables.
You may have noticed that we used a different way to declare our variables than was used in the Console Exe section.
You're correct. We just as easily could have declared them like this:
Either way is correct and is just a matter of personal preference.
After the definitions we can begin coding, or, in this case, use subroutines
to block the code. This means that we separate the major functional areas and call them as subroutines.
Our main program has two calls to subroutines (or functions): OpenMainWindow() and MainProcess()
The naming of these subroutines is, again, up to the user. Then we have the END statement which ends the program.
Your entry point or 'main', as it is often referred, will most likely be much more complex than this example.
Now, let's look at the subroutines we are calling.
The OpenMainWindow() subroutine:
When you run the program the OpenMainWindow() subroutine is called. The three Ebasic
commands in the subroutine are then executed:
1. OPENWINDOW creates a window. Notice the '&Win_Event_Handler' at the end
of the command line. This is a pointer (identified by the '&' )to the subroutine that this window
will use as its message handler, as discussed above. The name for the message handler subroutine is
one of the user's choosing.
2. SETWINDOWCOLOR sets the background color to blue.
3. CONTROL adds a button to the window and gives it an identifier of '1'.
The MainProcess() subroutine:
The WAITUNTIL command causes the program to remain at this point until the flag has a 0 (Off) value. Remember
that we set the flag to 1 (On) at the beginning of the program.
The immediate question that arises is "If the program remains here until the flag is set to 0, how does it ever set to
Between Ebasic and the Windows OS here is what is happening. The operating system periodically allows
each window a slice of time to process any messages that are pending for the window.
Ebasic is interfaced with that general process so it is given a piece of time to execute its
commands. It's extremely more complicated than that but this explanation should be all you really need to comprehend.
So, with this example, the Win_Event_Handler subroutine (message handler) will be repeatedly called once the
window (Main_Win) is created. If a message is to processed then the program will execute that thread of code.
If there are no messages the program returns to checking to see if the flag has been reset.
Let's now take a look at the actual message handler.
The Win_Event_Handler subroutine:
At first glance you can see that the internal structure is based upon the SELECT/CASE structure.
The SELECT statement says we are going to do something based upon the contents of the variable '@message'.
The CASE statements say compare the CASE value with that of the SELECT value. If they
are equal then execute the code following the CASE statement. All non-matches are ignored.
The first question raised is "Where does the value of @message come from?"
Variables preceded with a "@" are constants. @message is an Ebasic constant which will contain the id for the
message being processed (from the queue) on this pass through the handler subroutine. The Windows OS has 1,000s of different
messages. A small portion of them have been given unique Ebasic constant variable names. In our example
we use a few of the more common ones. It is important to remember that you only need to add CASE statements for events you
need to respond to in some way. If the default procedure accomplishes what you need then no entry required.
We will take a look at each SELECT/CASE statement in the example.
1. CASE @idclosewindow - This message is generated each time a window is instructed to close whether it is something you have
programmed in or the OS itself has done. This message is useful when there is some cleanup that needs to be done
before the window actually closes. In our case, when the user clicks on the "x" in the upper right corner of
our window, this message is generated by the Windows OS. We need to process this message so that the
Main_Win_Flag will be reset. The next time Ebasic has a time slice to execute the WAITUNTIL command if will find
the flag reset.
This, in turn, will cause the CLOSEWINDOW command to be executed. Since the window is already being closed
by the system the CLOSEWINDOW doesn't actually close the window but does do some internal EBasic cleanup.
NOTE: If the flag was not reset under the above conditions the program would become a Zombie and run in the background
with no way to close the process except to use the Windows Task Manager. You must take care when exiting programs
to always exit from the top level of the program.
2. CASE @idcreate - This message is generated each time a window is created, as we did above with the OPENWINDOW command.
We need to process this message in our example because we want to reposition our window to the center of the screen
with the Ebasic CENTERWINDOW command. With the values we created our window with, if we didn't center it, it would
appear in the upper left corner of our screen.
3. CASE @idchar - this message is generated by the OS each time a keyboard key is pressed while our window has the focus.
Since we wanted to use the 'ESC' key to close our window we had to add and process this message.
Many messages have data associated with them. When the OS sends this message it also sends the id of the key
that was pressed. Ebasic takes that information and places it in the Ebasic constant, @wparam.
We test @wparam to see if it was the 'ESC' key that was pressed. If so, we reset the flag and ultimately close the
window and end the program.
4. CASE @idcontrol - this message is generated each time a child control of the window sends a message because the
control itself had received a message. This message is the one that is used more often than any other.
This one looks different than the preceding ones because it contains a nested SELECT/CASE structure. That is
due to the fact that we need to know which control sent the message. The sending control's id is contained in
another Ebasic constant, @controlid. If you remember, when we used the CONTROL command and created our button, we
assigned it an id of '1'. The 'CASE 1' is the test to see if that was the control that sent the message.
If there is a match then we execute the code that follows. In this case we reset the flag and the window
closes and program ends.
NOTE: Although not demonstrated in this example, there is another Ebasic constant, @notifycode, that is important when
processing control messages. There are common instances where the OS generates several messages tied to a single
event. If there wasn't a way to distinguish between the messages a control receives and sends to the handler your
program would execute the same code multiple times. By testing the value of @notifycode you can eliminate that from happening.
This window does not do much but it does demonstrate the basic operation of a GUI program.
This simple foundation is the basis for all other functionality you may wish to add to the program.
If you understand this example you are well on your way to becoming an Ebasic Programmer.