30 May 2010

Initializing Variables

What value does a variable have when you first declare it, and what happens if you try to read it before you initialize it? And how do you get an initial value into a variable? 

Ocx initialization
Take a look at control and form properties. You initialize them as much as possible at design time in the Properties window. In contrast with VB, GFA-BASIC 32 needs extra code to initialize the item properties of every Ocx that supports a Item collection (ToolBar.Buttons; Status.Panels; etc). Initialization is so important to control and form properties that GFA-BASIC should be extended with more property pages to initialize items at design time. For example, we could use a property box to initialize the strings of a ListBox control at design time, a task you have to perform at run time now.

Initializing variables in general
In some languages, uninitialized variables have a semi-random value. In C, for example, local variables (but not global or static variables) are undefined. If you want an initial value, you must give it. Fortunately for C programmers, this is easy to do. An undefined variable is a disaster waiting to happen, and careful C coders initialize their variables as close to declarations as possible. In contrast, GFA-BASIC 32 always initializes all variables whenever they are declared. String variables are initialized to a "Null-String", numeric variables are initialized to 0, Variants are initialized to Empty, and object variables are initialized to Nothing.
This difference fits the philosophies of C and Basic. C doesn’t initialize variables to a default because local variables must be initialized at run time. This has a cost, and C doesn’t do any run-time work unless you ask for it. Undefined variables are dangerous, but that’s your problem. GFABASIC 32 is more concerned with safety. If you declare an array of 5000 Integers, GFA-BASIC 32 will initialize them all to 0 even if it takes extra run-time work to do so.
However, 0 or Empty might not be the initial value the program needs. In C, you can combine the declaration of a variable with its initialization:

int cLastWindow = 20;


Even arrays can combine a declaration and initialization. 

Fortunately, GFA-BASIC 32 allows declaration and initialization in one statement:

[Global] Dim iCountMax As Integer = 23


This usually works fine, and by using the Global statement global variables can be declared in any procedure. GFA-BASSIC 32 doesn't provide a 'Declarations section at the top' like VB, so we must take care when and where we declare global variables. The problem is that initialization-while-declaring requires executable code, the default value must be copied to the variable's memory location.

You need to find some logical place to put the declaration & initialization. That place must be reached only once—either when the program is executed or the first time the variable is accessed.
Global vs Static Variables
A Static variable is a global variable as well. The difference is that a global variable is visible to all parts of your program, while static variables have an implicit Local clause in front of it. They are only accessible inside a procedure. (Note - A Static variable in the main part of the program is local to the Main section only.) The Static variable can be initialized in a combination with a declaration:

Static fFirstTime As Boolean = True

A Static variable is only initialized once the first time the subroutine is executed. Would you use a global variable declaration & initialization inside a subroutine, the global variable would be initialized over and over.

16 May 2010

Virtual-key code to ANSI character

The Ocx_KeyDown event and the function Gfa_KeyGet (GFA-BASIC editor extension) provide a virtual-key code representing the key pressed. However, it doesn't take the shift-state into account. (The Screen_PreView event sub passes WM_KEYDOWN messages as well.)
In all cases you want to process a WM_KEYDOWN message the ANSI character isn't known. To convert a virtual-key code plus a shift state to an ANSI character you need ToAscii() API. This function takes the virtual-key code and the keyboard state and translates it to an ANSI character. I use it as follows:

' Press shift-key than click mouse
Do :   Sleep : Until MouseK
Trace Chr(VkKeyToAscii(65))
Trace Chr(VkKeyToAscii(Asc("8"))) ' –> *

Function VkKeyToAscii(keycode As Int) As Int
  Dim sb As String * 4

  Static Dim keyboardState(256) As Byte
  ~GetKeyboardState(ArrayAddr(keyboardState()))

  If ToAscii(keycode, 0, ArrayAddr(keyboardState()), sb, 0) == 1
    Return Asc(sb)
  Else
    Return 0
  EndIf

EndFunc

13 May 2010

DefMouse

DefMouse is an hold over from previous GFA-BASIC versions, but is still – with a little twist - supported in GFA-BASIC 32. In GFA-BASIC 16 bit DefMouse was used to set the mouse shape for the entire screen. DefMouse changed the mouse globally, for each GB window and control, as well as for the desktop. With GB32 the mouse-cursor shape is set for each OCX object individually, which is much more flexible. Now, the general way of setting a mouse-cursor is by setting the control's MousePointer, MouseCursor, or MouseIcon property, either at design-time or at runtime.

DefMouse 0 is the secret
GFA-BASIC 32 tries to be as compatible as can be with previous versions, so the DefMouse command had to be implemented as well. It works, however the region is now limited to GFA-BASIC windows (and controls) only. An improvement, wouldn't you agree? How does that work? How does GFABASIC prevent corrupting mouse pointers? The solution to the problem is the DefMouse value. When DefMouse == 0 (or basDefault) the individual mouse settings of the Forms and controls are used. However, when DefMouse n (where n <>0) is used to select a new mouse shape the individual OCX mouse settings are ignored. On the plus side, you are guaranteed with the new mouse shape over every(!) OCX control/form. To revert to the individual settings you must invoke DefMouse 0.

A Popup window
A Popup selection menu is a system window executed in the context of the GFA-BASIC 32 application. It isn't an Ocx object (it should have been!) and has no private mouse settings. Especially when a Popup is used from within a TrayIcon Ocx GFA-BASIC 32 has to revert to the DefMouse setting. However when DefMouse = 0 GFA-BASIC does nothing, because it expects to handle the individual Ocx mouse settings. The WM_SETCURSOR message isn't handled properly an a random cursor value is used to tell the system (Windows) which cursor to use. The mouse cursor is drawn incorrectly when showing the popup menu. Consequently, the global mouse must be set to a non-zero value before invoking Popup, for instance DefMouse basArrow. When Popup returns the global mouse setting must revert to zero: DefMouse 0.

Screen Ocx
The Screen Ocx supports the MousePointer, MouseCursor, or MouseIcon properties as well. These operate exactly the same as DefMouse, eg on the global mouse setting. Thus DefMouse can be replaced by Screen.MousePointer = n, where n is a basXX mouse value.
The DefMouse command can also be passed a string containing a bit-pattern to create a custom (monochrome) mouse.
The Screen.MouseCursor on the other hand accepts a MouseCursor object (a real Windows cursor resource in an external file). In addition, Screen.MouseIcon accepts a Picture object. So, there many different options to create a custom – global – mouse shape.