Introduction
Any real-world application will sooner or later misbehave. It is important to be able to find those problems. Visual FoxPro's built-in debugger can help a lot to find out why your program doesn't work as you thought it would. Most of the material in this article applies to Visual FoxPro 5 and later. The debugger has been redesigned for this version; Visual FoxPro 3, and FoxPro 2.x, use a more primitive debugger.
Starting the debugger
There are several ways to start the debugger:
A short sample
Here is an example of using ASSERT (which I saved as sample.prg):
set asserts on x = 0 assert x = 1 wait window "This is the last line"
On reaching the line with ASSERT, you get the dialog "Assertion failed".
The debugger windows
In the example above, if you click on "Debug", you get the main debugger Window. The main window contains five windows; the screenshot shows the two you will use most of the time: Trace and Watch. The available windows are:
Stepping through code
Two common and related tasks are stepping through code, and watching the values of expressions.
To step through code, one line at a time, you would click the "Step Into" button (or use the shortcut, F8). Here you can see how the program executes, one line at a time.
If a procedure calls another procedure, "Step Into" will show you the execution of the second procedure, line-by-line. If you want to avoid this (and only debug the original procedure), use the next button, "Step Over" (F6).
Note: Here, and in the remainder of this text, I will use the word "procedure" meaning "either a procedure, a function, a method, or an independent PRG".
The next button (Step Out, Shift-F7) is useful to quickly get out of the current procedure, and go to the next-higher level.
The next button (Run to Cursor, F7) can be used to run to a specific place in your code. You can also use breakpoints for this purpose.
In the case of objects, clicking on the "+" will show its properties and members. Each member object can again be opened, clicking on the "+".
One very useful object to debug is ThisForm, but this only works if the program is paused in the execution of a form method.
If Visual FoxPro is waiting for user input (usually with READ EVENTS), or executing some code outside the form (for instance, after clicking a toolbar button or selecting a menu item), ThisForm doesn't work. In this case, you can check properties of the form which is on top, with _vfp.ActiveForm.
Variables or properties whose value changed since you added them to the Watch window will appear in red.
One way to start debugging only at the desired point is to SET STEP ON (or use ASSERT) at the desired point. This doesn't require the debugger to be previously open.
Another way to start debugging at a specified point is to set breakpoints.
If you already see the desired procedure in the Trace window, you can double-click in the gray area to the left of a program statement. A red point will appear, indicating that program execution will pause at that point.
Once you set a breakpoint, you will usually want to press the "Resume" button (the first button on the debugger toolbar), or press F5, to run until a breakpoint is encountered.
You can also just type the name of the procedure or method in the Breakpoint window. Select "Tools | Breakpoints", leave "Type" as "Break at location", and under "Location", type the name of the procedure you want to debug.
This is very useful when you want to know what happens when a certain Visual FoxPro event occurs. For instance, you want to see what happens when you click a button.
Just set a breakpoint, on the location "Click".
You may also want to pause your program when the value of a certain expression changes. For instance, how come a certain control became disabled, when it is supposed to be enabled? Or how come it is ReadOnly? Or how come it isn't?
In the Debug window, you can set an expression on something like ThisForm.TxtSample.Enabled. When you double-click left of this expression (in the gray area), a breakpoint is automatically set. You can also set a breakpoint directly in the breakpoint window.
After this, you will want to click on "Resume", to continue program execution until the expression value changes.
General debugging strategy
The technical part, explained above, is the easy part. However, using the debugger to actually find the source of a problem requires experience, knowledge of Visual FoxPro commands, and quite often a bit of creativity. Often, it requires a lot of patience, too.
Sometimes it helps to work from the outside inwards: start debugging a procedure, debug line-by-line, and step over procedures, until you find that something weird happens - usually, a property or variable doesn't have the expected value.
If this happens right after running a procedure which you have "stepped over", repeat, but this time go into the procedure. You may have to repeat this several times, going deeper and deeper into procedures.
At other times, you need to go from the inside outwards: you start with a suspicious procedure, which you might have set as a breakpoint. Then, you notice that the error happened previously, so you repeat the debugging, this time stopping at a higher-level procedure.
Just setting a breakpoint for the case that a value changes (or reaches a certain value) can often quickly locate a problem.
Some events are very hard to debug. For instance, Activate and Deactivate fire when you change from one window to another - and this can happen if you simply change between the main Visual FoxPro window and the Debugger window.
MouseMove fires continuously while you move the mouse, InteractiveChange fires with every keystroke, and a Timer event can fire too often for comfortable debugging.
In cases like these, instead of using the built-in debugger, it sometimes helps to temporarily additional commands in the code.
One possibility is the ASSERT command, which only shows something if a condition is not fulfilled.
Another possibility is to show information on screen. For instance, WAIT WINDOW "Refresh" TIMEOUT 1 in a Refresh() Event can help you verify whether the Refresh() Event is reached at all, or not.
Or, you can send output to the corresponding debugger window with the DEBUGOUT command, or write information to a text file. This is easily done with FileToStr().
Conclusion
Sometimes just stepping through the code with the debugger will immediately highlight a problem. At other times, you will have to investigate for hours, sometimes days, until you find the cause of a problem. Whatever the case may be, it helps to know some debugging techniques.