In order to get a better understanding of how everything fits together we need to look "under the hood", so to speak, and see what is going on that we don't normally see.
The Windows API is a collection of functions, provided by the OS, that allows us to have a graphic interface to our programs. These functions are often cryptic and can be extremely complex for the casual programmer.
As previously stated, IWBasic commands, in general, make our programming life simpler. They tend to isolate us from the API. The IWB command may be very close in syntax to the API function. The SENDMESSAGE command is a good example. It is simply a 'wrapper' that takes IWB type variables and converts them to the proper format before calling the API SendMessage function. Simple and straightforward. Some IWB commands have the same name as an API function. These naming conflicts are commonly resolved via an "alias" declaration for the API function and the prepending of an underscore '_' to the function name. That is why you will see those underscored functions scattered through the sample files.
Other IWB commands invoke several API functions while performing its designated function. Often, the IWB command is calling functions that we are totally unaware of.
We have already discussed three IWB commands that are of this more complex nature:
The most important API function called by all three of these IWBasic commands is CreateWindowExA. This function is the one that actually creates the object (window/control).
In the previous discussion about messages we learned that to the operating system it didn't matter whether a window or control was sending/receiving messages. To the operating system they were the same. We now know why. They are created the same. That leads us to the old adage that "if it walks like a duck and quacks like a duck then it must be a duck."
Let's examine the CreateWindowExA API function in relation to the three IWBasic commands above:
CreateWindowExA(exStyle_flags, class, title, style_flags, left, top, width, height, parent, hMenu, hInstance, lpParam),INT
OPENWINDOW variable, left, top, width, height, style_flags, parent, title, handler
CONTROL parent, type, title, left, top, width, height, style_flags, id
CONTROLEX parent, class, title, left, top, width, height, style_flags, exStyle_flags, id
We'll start by removing parameters that are common to all four entries and that have been previously discussed.
- left, top, width, height
That leaves us with following parameters for each:
CreateWindowExA(exStyle_flags, class, hMenu, hInstance, lpParam),UINT
OPENWINDOW variable, handler
CONTROL type, id
CONTROLEX class, exStyle_flags, id
The CreateWindowExA returns a window handle when it called successfully. We know from previous discussions that that handle is stored in the variable UDT element 'hWnd' when using OPENWINDOW. We also know that when dealing with controls the handle is tied to the control's id and shows up in the @LPARAM of a message.
The CreateWindowExA parameters hMenu and lpParam are used internally by IWBasic and the OS and have different purposes depending upon whether we are dealing with what we call a dialog or window; or a regular window vs. a MDI window; or a control. It is beyond the scope of this tutorial to go into those details.
hInstance is the handle to the current module/application.
Removing all of those we have:
CONTROLEX class, exStyle_flags
Suffice it to say that the OPENWINDOW and CONTROL commands automatically assign some exStyle_flags based upon what type of object we are dealing with. Setting those aside we are left with:
CreateWindowExA requires a class parameter in order to create an object, be it a window or control. From the section on controls we know that the class parameter is "a string that identifies the specific type of control being added". Since it has to apply to both windows and controls we can modify that definition to "a string that identifies the specific type of object being added".
Looking at the source code for OPENWINDOW we find that the command is assigning the class for us.
For what we refer to as a normal IWBasic window the class is "IWBasicWndClass". When creating a window with the @MDIFRAME style the frame is assigned the "IWBasicFrameClass" class. At the same time the associated client window is assigned the "MDICLIENT" class.
The CONTROL command has a SELECT/ENDSELECT block that looks at the type constant passed to the command. The following lists each predefined type and the corresponding automatically assigned class:
IWBasic TYPE Constant Class
Notice that the first six types are all of the same class. What makes them appear different when they are created is style_flags. The CONTROL command automatically assigns the proper style_flag for the desired control.
The is a good time to note that the OPENWINDOW and CONTROL commands also call API functions to automatically set default fonts, foreground/background colors as well as appropriate exstyle_flags.
Well, that's all fine and good, but what is the class doing for us? Why do we need to be concerned about it?
Remember back in the sections on messages and message handlers. We said that the OS sorts out what messages get sent where. Well, when we create an object we tell the OS which handler to use for that object and what messages to send to the handler.
But wait you say, the CreateWindowExA, CONTROL, and CONTROLEX don't have a message handler variable. Only the OPENWINDOW command has anything identifying a message handler. I know it may seem strange when I say that the OPENWINDOW command is the special case here and the CONTROL and CONTROLEX commands are the more normal cases.
Each class has a dedicated message handler tied to it. The class name is simply a way to identify which handler to use.
But wait, we define a message handler when we use OPENWINDOW. Something doesn't sound right. The window handler defined with the OPENWINDOW is there so that it is easy for us to integrate our code with the real windows handler defined by IWBasic that we can't see. This setup makes it easier on us to do our thing, code wise. That is what allows us to pick our own name for a handler routine.
Fine, but what about message handlers for individual controls. We don't define them anywhere.
But they are. When a control is created (in this case we are talking about when it is designed initially) it has a message handler that is an integral part of the control. It totally defines the control; what it looks like; what it responds to; everything. The creator of the control has to give the control a unique class name to identify it, in addition, it requires a unique handler name which the ultimate user of the control will never see.
The one missing piece we haven't touched on is the RegisterClassExA API function. For every class this function has to be called to provide the OS the necessary information to functionally merge the object into the system. Although your application may contains hundreds of objects of a given class (or type) the class has to be registered only once. The function passes a UDT type OS structure with, among other things, the class name and a pointer to the default message handler subroutine. We will cover the structure in more detail when we start developing the code for our custom control.
For the standard controls the registering of the class names and the default handlers is taken care of by the system. However, IWBasic does sub-class some of the handlers in order to change certain aspects of the regular OS controls. Sub-classing is not within the scope of this tutorial.
To conclude our review we summarize as follows:
When a GUI application is built we create windows and controls that generate and/or respond to event messages. Part of what happens when events occur is determined by the OS system and part can be determined by the user. Some events are responded to in a manner predefined by IWBasic. We can create our own windows and/or controls that act totally the way we desire by creating a new class and handler and registering that class with the system. But, when we choose to do that, we loose a lot of the ease of coding that IWBasic normally provides us. However, we are ultimately providing new functionality for user's of our creations along with the same normal ease of use that IWBasic normally provides.
Hopefully, at this point, the reader has a better understanding of how IWBasic and the OS are both dealing with windows/controls.
I highly suggest that the Windows Programming section of IWBasic's User's Guide be reread before proceeding, to reinforce what has been written in this Review section.
Coming Next - User's Control Spec