- Remove From My Forums
-
Question
-
FOR XX = 1 TO 15 STEP 1
*
*
*
NEXT XX && <— Error is here rarelyUsing the For/Next statement, the Nesting error arrises
rarely
why this happens ?
Answers
-
If you try to compile this you get such a compile error:
For xx = 1 to 10
If xx=1
Else
Next xxWhat’s missing is no not NEXT or ENDFOR, but ENDIF. So look out for any nesting error in code inside the loop.
Bye, Olaf.
Olaf Doschke — TMN Systemberatung GmbH http://www.tmn-systemberatung.de
-
Proposed as answer by
Saturday, December 13, 2014 9:38 AM
-
Marked as answer by
Ed Price — MSFTMicrosoft employee
Monday, March 16, 2015 8:23 AM
-
Proposed as answer by
-
LOCAL ii as integer FOR ii = 1 TO 15 STEP 1 WAIT WINDOW "ii = " + STR(ii, 4) NEXT ii
The above replicates the FOR loop you are using.
I have not received any errors after numerous runs of the above.
One thing to point out is that the NEXT ii statement is technically incorrect.
It should be just NEXT — so
LOCAL ii as integer FOR ii = 1 TO 15 STEP 1 WAIT WINDOW "ii = " + STR(ii, 4) NEXT
The English Bob
-
Proposed as answer by
Ed Price — MSFTMicrosoft employee
Saturday, December 13, 2014 9:38 AM -
Marked as answer by
Ed Price — MSFTMicrosoft employee
Monday, March 16, 2015 8:23 AM
-
Proposed as answer by
-
What means «rarely»? Are you able to reproduce this error? If not then you cannot fix it probably. And I can say it is either a bug in your code or in VFP itself.
NEXT can generate Nesting error if the FOR command fails or if the VFP forgets about it etc. etc.
Did you try the VFP Compiler from
baiyujia? Does the bug appears in compiled code?-
Edited by
Pavel Celba
Thursday, October 30, 2014 11:34 PM
link added -
Proposed as answer by
Ed Price — MSFTMicrosoft employee
Saturday, December 13, 2014 9:39 AM -
Marked as answer by
Ed Price — MSFTMicrosoft employee
Monday, March 16, 2015 8:23 AM
-
Edited by
-
I remembered and found a tek-tips thread I wrote about having ERROR 96 «Nesting Error» at runtime.
http://www.tek-tips.com/viewthread.cfm?qid=1722110
The problem was never resolved, but in my case remained a one time only error. If ou have his rarely, but still too often, the best thing you may do is rewrite that passage to something else. Perhaps you put the large CASE statement into a procedure or other
method.Taken from the tek-tips thread, one thinkable way to have this error at runtime is causing the issue mentioned in the help, an EXIT outside of a loop.
This here compiles and errors at runtime with error 96:
lcMacro = "Exit" &lcMacro
Nevertheless, this wasn’t my problem, nor do I think you do something causing an error by macro substitution.
It’s strange enough, that there is a native error number 96, while this rather is an error not thinkable at runtime, if there is no nesting error found at compile time. And compile time errors don’t have an error number, they are output and in case they’re
fatal they stop compilation.Bye, Olaf.
Olaf Doschke — TMN Systemberatung GmbH http://www.tmn-systemberatung.de
-
Proposed as answer by
Ed Price — MSFTMicrosoft employee
Saturday, December 13, 2014 9:39 AM -
Marked as answer by
Ed Price — MSFTMicrosoft employee
Monday, March 16, 2015 8:23 AM
-
Proposed as answer by
-
To evaluate a nesting error, you have to evaluate the entire program. Sometimes the error can be caused in a program called after it (which makes it a little bit tricky). For example, your program is calling another procedure
Fernando, even when your comment is correct, about no need to put the variable after NEXT, i.e. FOR x=1 to 4 … NEXT xx, could be read as just NEXT; adding the variable, will make it easier to read, specially with a big code inside
As per procedure, I always add a comment when closing SCAN..ENDSCAN, DO WHILE..ENDDO, FOR..NEXT of which variables I am referring to, and many cases add the condition on the comment (at the end)
-
Proposed as answer by
Ed Price — MSFTMicrosoft employee
Saturday, December 13, 2014 9:39 AM -
Marked as answer by
Ed Price — MSFTMicrosoft employee
Monday, March 16, 2015 8:24 AM
-
Proposed as answer by
-
One computer, 3 out of 100 … you should also think about hardware problems.
-
Marked as answer by
Ed Price — MSFTMicrosoft employee
Monday, March 16, 2015 8:24 AM
-
Marked as answer by
Содержание
- Nesting error in foxpro
- Nesting error in foxpro
- Nesting error in foxpro
- Answered by:
- Question
- Answers
- Nesting error in foxpro
- Answered by:
- Question
- Answers
- Nesting error in foxpro
Nesting error in foxpro
Вот спасибо! Не знал об этом. 
Запускаю форму так:
Для удобства, решил что имя файла, свойство Form.NAME и имя объектной переменной будут иметь одинаковое значение. В конкретном случае это ‘Spr_Unit’. Насчёт более чем одного запуска формы — логикой программы эта возможност исключена.
Грид лежит на форме, которая является экземпляром пользовательсого класса, наследника класса Form. И он удачно «находится».
Исправлено 3 раз(а). Последнее : Dutch, 08.11.12 15:59

И если ты так тщательно контролируешь точку запуска форм, то зачем делать это через ПЕРЕМЕННЫЕ? Ещё раз — если твоя форма не модальная, то после запуска код пойдёт дальше, дойдёт до конца модуля (процедура это, функция, метод — не суть важно) и там переменная перестанет существовать.
Лучше уж тогда организовать СВОЮ коллекцию/массив для хранения ссылок на запускаемые формы (у «своего» есть плюсы по сравнению с встроенными _VFP/_SCREEN.Forms — например можно не все формы туда помещать, можно явно задавать свой «идентификатор для поиска экземпляра»). И, естественно, обеспечить должную область видимости для этой коллекции/массива (банально PUBLIC, или добавить её к _SCREEN или к «своему объекту приложения goApp»).
100500 private переменных непонятно когда создаваемых и непонятно когда уничтожаемых (или приобретающих в качестве значения .NULL. — если форму закрыли) до добра не доведут. «непонятно когда» — в смысле использующего их потом кода. Например из меню НЕЛЬЗЯ безопасно обратиться к такой переменной, из некоего метода базового класса — тоже.
Кстати, сама идея обращаться к объектам формы «снаружи» дурно попахивает. С объектами формы должна работать сама форма — а для её методов всегда доступен This/ThisForm — и никаких «переменных» не нужно.

| Re: Что означает ошибка Nesting error? |
|---|
| Igor Korolyov
Сообщений: 34580 |

| Re: Что означает ошибка Nesting error? |
|---|
| Dutch
Сообщений: 212 |
Жесть. Назачем тогда вообще это меню надо 
Источник
Nesting error in foxpro
Вот спасибо! Не знал об этом. 
Запускаю форму так:
Для удобства, решил что имя файла, свойство Form.NAME и имя объектной переменной будут иметь одинаковое значение. В конкретном случае это ‘Spr_Unit’. Насчёт более чем одного запуска формы — логикой программы эта возможност исключена.
Грид лежит на форме, которая является экземпляром пользовательсого класса, наследника класса Form. И он удачно «находится».
Исправлено 3 раз(а). Последнее : Dutch, 08.11.12 15:59

| Re: Что означает ошибка Nesting error? |
|---|
| Igor Korolyov
Сообщений: 34580 |
| Re: Что означает ошибка Nesting error? |
|---|
| Igor Korolyov Автор |
Сообщений: 34580
Дата регистрации: 28.05.2002
И если ты так тщательно контролируешь точку запуска форм, то зачем делать это через ПЕРЕМЕННЫЕ? Ещё раз — если твоя форма не модальная, то после запуска код пойдёт дальше, дойдёт до конца модуля (процедура это, функция, метод — не суть важно) и там переменная перестанет существовать.
Лучше уж тогда организовать СВОЮ коллекцию/массив для хранения ссылок на запускаемые формы (у «своего» есть плюсы по сравнению с встроенными _VFP/_SCREEN.Forms — например можно не все формы туда помещать, можно явно задавать свой «идентификатор для поиска экземпляра»). И, естественно, обеспечить должную область видимости для этой коллекции/массива (банально PUBLIC, или добавить её к _SCREEN или к «своему объекту приложения goApp»).
100500 private переменных непонятно когда создаваемых и непонятно когда уничтожаемых (или приобретающих в качестве значения .NULL. — если форму закрыли) до добра не доведут. «непонятно когда» — в смысле использующего их потом кода. Например из меню НЕЛЬЗЯ безопасно обратиться к такой переменной, из некоего метода базового класса — тоже.
Кстати, сама идея обращаться к объектам формы «снаружи» дурно попахивает. С объектами формы должна работать сама форма — а для её методов всегда доступен This/ThisForm — и никаких «переменных» не нужно.


| Re: Что означает ошибка Nesting error? |
|---|
| Dutch
Сообщений: 212 |
| Re: Что означает ошибка Nesting error? |
|---|
| Igor Korolyov Автор |
Сообщений: 34580
Дата регистрации: 28.05.2002
Жесть. Назачем тогда вообще это меню надо 
Источник
Nesting error in foxpro
This forum has migrated to Microsoft Q&A. Visit Microsoft Q&A to post new questions.
![]()
Answered by:

Question


FOR XX = 1 TO 15 STEP 1
*
*
*
NEXT XX && rarely
why this happens ?
Answers


If you try to compile this you get such a compile error:
For xx = 1 to 10
If xx=1
Else
Next xx
What’s missing is no not NEXT or ENDFOR, but ENDIF. So look out for any nesting error in code inside the loop.
Olaf Doschke — TMN Systemberatung GmbH http://www.tmn-systemberatung.de
- Proposed as answer by Ed Price — MSFT Microsoft employee Saturday, December 13, 2014 9:38 AM
- Marked as answer by Ed Price — MSFT Microsoft employee Monday, March 16, 2015 8:23 AM


The above replicates the FOR loop you are using.
I have not received any errors after numerous runs of the above.
One thing to point out is that the NEXT ii statement is technically incorrect.
It should be just NEXT — so
The English Bob
- Proposed as answer by Ed Price — MSFT Microsoft employee Saturday, December 13, 2014 9:38 AM
- Marked as answer by Ed Price — MSFT Microsoft employee Monday, March 16, 2015 8:23 AM


What means «rarely»? Are you able to reproduce this error? If not then you cannot fix it probably. And I can say it is either a bug in your code or in VFP itself.
NEXT can generate Nesting error if the FOR command fails or if the VFP forgets about it etc. etc.
Did you try the VFP Compiler from baiyujia ? Does the bug appears in compiled code?
- Edited by Pavel Celba Thursday, October 30, 2014 11:34 PM link added
- Proposed as answer by Ed Price — MSFT Microsoft employee Saturday, December 13, 2014 9:39 AM
- Marked as answer by Ed Price — MSFT Microsoft employee Monday, March 16, 2015 8:23 AM


I remembered and found a tek-tips thread I wrote about having ERROR 96 «Nesting Error» at runtime.
The problem was never resolved, but in my case remained a one time only error. If ou have his rarely, but still too often, the best thing you may do is rewrite that passage to something else. Perhaps you put the large CASE statement into a procedure or other method.
Taken from the tek-tips thread, one thinkable way to have this error at runtime is causing the issue mentioned in the help, an EXIT outside of a loop.
This here compiles and errors at runtime with error 96:
Nevertheless, this wasn’t my problem, nor do I think you do something causing an error by macro substitution.
It’s strange enough, that there is a native error number 96, while this rather is an error not thinkable at runtime, if there is no nesting error found at compile time. And compile time errors don’t have an error number, they are output and in case they’re fatal they stop compilation.
Источник
Nesting error in foxpro
This forum has migrated to Microsoft Q&A. Visit Microsoft Q&A to post new questions.
Answered by:

Question


FOR XX = 1 TO 15 STEP 1
*
*
*
NEXT XX && rarely
why this happens ?
Answers


If you try to compile this you get such a compile error:
For xx = 1 to 10
If xx=1
Else
Next xx
What’s missing is no not NEXT or ENDFOR, but ENDIF. So look out for any nesting error in code inside the loop.
Olaf Doschke — TMN Systemberatung GmbH http://www.tmn-systemberatung.de
- Proposed as answer by Ed Price — MSFT Microsoft employee Saturday, December 13, 2014 9:38 AM
- Marked as answer by Ed Price — MSFT Microsoft employee Monday, March 16, 2015 8:23 AM


The above replicates the FOR loop you are using.
I have not received any errors after numerous runs of the above.
One thing to point out is that the NEXT ii statement is technically incorrect.
It should be just NEXT — so
The English Bob
- Proposed as answer by Ed Price — MSFT Microsoft employee Saturday, December 13, 2014 9:38 AM
- Marked as answer by Ed Price — MSFT Microsoft employee Monday, March 16, 2015 8:23 AM


What means «rarely»? Are you able to reproduce this error? If not then you cannot fix it probably. And I can say it is either a bug in your code or in VFP itself.
NEXT can generate Nesting error if the FOR command fails or if the VFP forgets about it etc. etc.
Did you try the VFP Compiler from baiyujia ? Does the bug appears in compiled code?
- Edited by Pavel Celba Thursday, October 30, 2014 11:34 PM link added
- Proposed as answer by Ed Price — MSFT Microsoft employee Saturday, December 13, 2014 9:39 AM
- Marked as answer by Ed Price — MSFT Microsoft employee Monday, March 16, 2015 8:23 AM


I remembered and found a tek-tips thread I wrote about having ERROR 96 «Nesting Error» at runtime.
The problem was never resolved, but in my case remained a one time only error. If ou have his rarely, but still too often, the best thing you may do is rewrite that passage to something else. Perhaps you put the large CASE statement into a procedure or other method.
Taken from the tek-tips thread, one thinkable way to have this error at runtime is causing the issue mentioned in the help, an EXIT outside of a loop.
This here compiles and errors at runtime with error 96:
Nevertheless, this wasn’t my problem, nor do I think you do something causing an error by macro substitution.
It’s strange enough, that there is a native error number 96, while this rather is an error not thinkable at runtime, if there is no nesting error found at compile time. And compile time errors don’t have an error number, they are output and in case they’re fatal they stop compilation.
Источник
Nesting error in foxpro

| Re: Что означает ошибка Nesting error? |
|---|
| Dutch Автор |
Сообщений: 212
Откуда: Волгодонск
Дата регистрации: 25.10.2010
Такие дела: хочу определить присутствует ли на форме грид. Есть название формы которое хранится в FormName. Делаю это так:
Если условие IF выполняется при Первой итерации, то MESSAGEBOX отрабатывается корректо и покидаем цикл. Если же при первой итерации мы не попадаем внутрь IF, то на комманде ENDFOR появляется ошибка «Nesting error».
Если же указываем имя формы явно, то ошибка «Nesting error»не появляется.
Исправлено 4 раз(а). Последнее : Dutch, 07.11.12 20:36

Ох уж эти ма. ма. мааакросы.
И как люди программируют на языках где их нет?

| Re: Что означает ошибка Nesting error? |
|---|
| _vit
Сообщений: 5033 |
«Название формы» (кстати, что именно ты под этим понимаешь — такого термина НЕТУ в фоксе. Есть имя файла формы — т.е. MyForm.scx, и есть свойство формы Name — например frmMyTestForm) никак не связано с переменной — объектной ссылкой на созданный экземпляр формы. Да, по умолчанию команда DO FORM создаёт Private переменную с именем аналогичным имени файла формы (т.е. MyForm для этого примера) — но вся соль в том, что эта переменная Private, а значит код запросто может выйти за ту область видимости где эта переменная существует, ну или же эта переменная будет явно перекрыта другой, одноименной.
Вообще ПОДОБНЫЕ ссылки на исполняющиеся формы — дурной тон. Уж лучше использовать _VFP.Forms(«frmMyTestForm»), или даже цикл обхода по _VFP.Forms/_SCREEN.Forms, т.к. может быть запущено много экземпляров одной и той же формы.

| Re: Что означает ошибка Nesting error? |
|---|
| Igor Korolyov
Сообщений: 34580 |

| Re: Что означает ошибка Nesting error? |
|---|
| XAndy
Сообщений: 3803 |
| Re: Что означает ошибка Nesting error? |
|---|
| Dutch Автор |
Сообщений: 212
Откуда: Волгодонск
Дата регистрации: 25.10.2010
Вот спасибо! Не знал об этом. 
Запускаю форму так:
Для удобства, решил что имя файла, свойство Form.NAME и имя объектной переменной будут иметь одинаковое значение. В конкретном случае это ‘Spr_Unit’. Насчёт более чем одного запуска формы — логикой программы эта возможност исключена.
Грид лежит на форме, которая является экземпляром пользовательсого класса, наследника класса Form. И он удачно «находится».
Исправлено 3 раз(а). Последнее : Dutch, 08.11.12 15:59

И если ты так тщательно контролируешь точку запуска форм, то зачем делать это через ПЕРЕМЕННЫЕ? Ещё раз — если твоя форма не модальная, то после запуска код пойдёт дальше, дойдёт до конца модуля (процедура это, функция, метод — не суть важно) и там переменная перестанет существовать.
Лучше уж тогда организовать СВОЮ коллекцию/массив для хранения ссылок на запускаемые формы (у «своего» есть плюсы по сравнению с встроенными _VFP/_SCREEN.Forms — например можно не все формы туда помещать, можно явно задавать свой «идентификатор для поиска экземпляра»). И, естественно, обеспечить должную область видимости для этой коллекции/массива (банально PUBLIC, или добавить её к _SCREEN или к «своему объекту приложения goApp»).
100500 private переменных непонятно когда создаваемых и непонятно когда уничтожаемых (или приобретающих в качестве значения .NULL. — если форму закрыли) до добра не доведут. «непонятно когда» — в смысле использующего их потом кода. Например из меню НЕЛЬЗЯ безопасно обратиться к такой переменной, из некоего метода базового класса — тоже.
Кстати, сама идея обращаться к объектам формы «снаружи» дурно попахивает. С объектами формы должна работать сама форма — а для её методов всегда доступен This/ThisForm — и никаких «переменных» не нужно.

| Re: Что означает ошибка Nesting error? |
|---|
| Igor Korolyov
Сообщений: 34580 |
| Re: Что означает ошибка Nesting error? |
|---|
| Dutch Автор |
Сообщений: 212
Откуда: Волгодонск
Дата регистрации: 25.10.2010

Жесть. Назачем тогда вообще это меню надо 
Источник
| Re: Что означает ошибка Nesting error? |
|---|
| Igor Korolyov
Сообщений: 34580 |
Nesting error at runtime?
(OP)
19 Nov 13 09:12
The strangest things happen lately:
I get error 96 (Nesting error) ereported in a certain inherited method, but in no particular line number (lineno() is 0)
This seems very weird to me, as a nesting error would stop the compilation at build time and would never be able to happen at runtime. The only way a nesting error could be expected at runtime is due to Execsript(), which is not in play here. Even macro substitution cannot cause it, as you can’t substitute anything needing multi line code to be nested at all.
So the master question is: In what circumstances could a nesting error occur?
One detail: The method in question is called PreSaveHook and called from the save method of a data access class:
CODE
If Not This.PreSaveHook() *--- If presave conditions fail, cancel save Return CANCEL Endif
This is the lowest nesting level of the save method, so no nesting can be broken on this level, too. One further mentionable thing is, that data access objects may be «stacked», in that a parent object starts a transaction and trigger child objects save, after itself successfully saved.
At least it’s not urgent, because it wasn’t reproducable, but it would good to know how to get at the source of a nesting error, if one happens. ASTACKINFO is on my list, I thought it’s already used in the general error handler, but no stack info is reported in the error screenshot. Line 0 is something I normally only get, if a database trigger fails, but in this case the backend is MS SQL Server, not a DBF of a DBC.
Could a network error cause some object to fail and cause a nesting error?
Bye, Olaf.
![]()
| *** | *bad date* |
| *** | Cannot allocate screen map. |
| *** | Cannot create program workspace.
|
| *** | Cannot find spell check program. |
| *** | Cannot open configuration file. |
| *** | Cannot run on MS-DOS before version 3.0. |
| *** | Fatal error <expN> reporting error <expN>. |
| *** | *Interrupted*
|
| *** | Invalid compact EXE file. Rebuild EXE. |
| *** | Invalid or missing EXE file. |
| *** | Logical expression required. |
| *** | Popup too big, first <expN> entries shown. |
| *** | Product has not been properly installed.
|
| *** | Source code not found. |
| *** | Source code out of date. |
| *** | Unable to locate desired version of Visual FoxPro. |
| *** | Unable to process error. |
| *** | Unknown error code <expN>.
|
0001
| 1 | File does not exist. |
| 3 | File is in use. |
| 4 | End of file encountered. |
| 5 | Record is out of range.
|
| 6 | Too many files open. |
| 7 | File already exists. |
| 9 | Data type mismatch. |
| 10 | Syntax error. |
| 11 | Function argument value, type, or count is invalid. |
| 12 | Variable «<variable>» is not found. |
| 13 | Alias is not found. |
| 15 | Not a table. |
| 16 | Unrecognized command verb. |
| 17 | Table number is invalid. |
| 18 | Line is too long. |
| 19 | Index file does not match table. |
| 20 | Record is not in index. |
| 21 | Length of memory variable strings exceeds amount of memory. |
| 22 | Too many memory variables. |
| 23 | Index expression exceeds maximum length. |
| 24 | Alias name is already in use. |
| 26 | Table has no index order set. |
| 27 | Not a numeric expression. |
| 30 | Row or column position is off the screen. |
| 31 | Invalid subscript reference. |
| 34 | Operation is invalid for a Memo, General, or Picture field. |
| 36 | Command contains unrecognized phrase/keyword. |
| 37 | You must use a logical expression with FILTER. |
| 38 | Beginning of file encountered. |
| 39 | Numeric overflow. Data was lost. |
| 41 | Memo file is missing or is invalid. |
| 42 | The LOCATE command must be issued before the CONTINUE command. |
| 43 | There is not enough memory to complete this operation. |
| 44 | Cyclic relation. |
| 45 | Not a character expression. |
| 46 | Expression evaluated to an illegal value. |
| 47 | No fields found to process. |
| 50 | Report file is invalid. |
| 52 | No table is open. |
| 54 | Label file is invalid. |
| 55 | Memory variable file is invalid. |
| 56 | Not enough disk space for «<file name>». |
| 58 | LOG( ): Zero or negative used as argument. |
| 61 | SQRT argument cannot be negative. |
| 62 | Cannot access characters beyond string. |
| 67 | Expression evaluator failed. |
| 78 | ** or ^ domain error. |
| 91 | File was not placed in memory using the LOAD command. |
| 94 | Must specify additional parameters. |
| 95 | Statement is not allowed in interactive mode. |
| 96 | Nesting error. |
0100
| 101 | Cannot open file «<file>». |
| 102 | Cannot create file «<file>». |
| 103 | Allowed DO nesting level exceeded. |
| 104 | Unknown function key. |
| 107 | Operator/operand type mismatch. |
| 108 | File is in use by another user. |
| 109 | Record is in use by another user. |
| 110 | File must be opened exclusively. |
| 111 | Cannot update the Cursor object. |
| 112 | Invalid key length. |
| 114 | Index does not match the table. Delete the index file and re-create the index. |
| 115 | .DIF file header is invalid. |
| 116 | .DIF vector is invalid — .DBF field mismatch. |
| 117 | .DIF type indicator is invalid. |
| 119 | .SYLK file header is invalid. |
| 120 | .SYLK file dimension bounds are invalid. |
| 121 | .SYLK file format is invalid. |
| 124 | Printer redirection is invalid. |
| 125 | Printer is not ready. |
| 127 | View file is invalid. |
| 130 | Record is not locked. |
| 138 | No fields were found to copy. |
| 152 | Missing expression. |
| 164 | Menu title has not been defined with DEFINE PAD. |
| 165 | Menu has not been defined with DEFINE POPUP. |
| 166 | No menu items have been defined for this menu. |
| 167 | Menu item position must be a positive number. |
| 168 | Menu has not been defined with DEFINE MENU. |
| 169 | Cannot define menu item. |
| 170 | Cannot release menu item. |
| 174 | Cannot redefine menu in use. |
| 175 | Cannot redefine a popup that is in use. |
| 176 | Cannot clear menu that is in use. |
| 177 | Cannot clear popup that is in use. |
| 178 | Menu has not been activated with ACTIVATE MENU. |
| 179 | Menu has not been activated with ACTIVATE POPUP. |
| 181 | Menu specified in ACTIVATE MENU is already in use. |
| 182 | Menu specified in ACTIVATE POPUP is already in use. |
0200
| 202 | Invalid path or file name. |
| 214 | Window «<name>» has not been defined with DEFINE WINDOW. |
| 215 | Window has not been activated with ACTIVATE WINDOW. |
| 216 | Display mode is not available. |
| 221 | Left margin including indent must be less than the right margin. |
| 222 | Line number must be less than page length. |
| 223 | Column number must be between 0 and 255. |
| 225 | «<name>» is not a memory variable. |
| 226 | «<name>» is not a file variable. |
| 227 | Box dimensions are invalid. |
| 228 | Tab stops must be in ascending order. |
| 230 | Array dimensions are invalid. |
| 231 | Invalid argument used with the SET function. |
| 255 | Not a valid RapidFile database. |
| 256 | Not a valid Framework II database/spreadsheet. |
| 279 | Menu was not pushed. |
| 287 | Menu size is too small. |
| 291 | Expression used with ASIN( ) is out of range. |
| 292 | Cannot use 0 or negative as the argument for LOG10( ). |
| 293 | Expression used with ACOS( ) is out of range. |
| 297 | Lotus 1-2-3 version 2.0 file format is invalid. |
0300
1000
1100
| 1102 | Cannot create file. |
| 1103 | Invalid seek offset. |
| 1104 | Error reading file. |
| 1105 | Error writing to file. |
| 1106 | Cannot access file; transaction is in progress. |
| 1108 | Picture too big, corrupt, or in wrong format. |
| 1111 | Invalid file descriptor. |
| 1112 | Error closing the file. |
| 1113 | File is not open. |
| 1115 | Invalid operation for the Cursor object. |
| 1117 | Key field length does not match. |
| 1124 | Key exceeds allowed size. |
| 1126 | Record is too long. |
| 1127 | You must use a logical expression with a FOR or WHILE clause. |
| 1130 | ‘field’ phrase is not found. |
| 1131 | The file specified in the FROM clause is empty. |
| 1134 | Variable must be in the selected table. |
| 1140 | FILTER expression exceeds allowed size. |
| 1141 | Unrecognized file revision. Re-create the index. |
| 1145 | Must be a character or numeric key field. |
| 1147 | Target table is already engaged in a relation. |
| 1148 | Expression has been re-entered while the filter is executing. |
| 1149 | Not enough memory for buffer. |
| 1150 | Not enough memory for file map. |
| 1151 | Not enough memory for filename. |
| 1152 | Cannot access the selected table. |
| 1153 | Cannot rename the file to different device. |
| 1156 | Duplicate field names. |
| 1157 | Cannot update the file. |
| 1161 | Too many records to browse or edit in the demo version. |
| 1162 | Procedure «<procedure>» is not found. |
| 1163 | Browse table is closed. |
| 1164 | Browse structure has changed. |
| 1165 | «<field>» is not related to the current work area. |
| 1166 | Cursor is corrupt or in wrong format. |
| 1167 | Icon is corrupt or in wrong format. |
| 1168 | Could not add icon to executable file. |
| 1169 | Project file is read-only. |
| 1178 | Application file «<file>» is not closed. |
| 1193 | Missing .RTT section. |
| 1194 | Link command failed. |
| 1195 | Object file «<file>» was compiled in a previous version of FoxPro. |
| 1196 | «<file>» is not a Visual FoxPro .EXE file. |
1200
| 1201 | Too many names used. |
| 1202 | Program is too large. |
| 1206 | Recursive macro definition. |
| 1211 | ELSE or ENDIF does not have corresponding IF statement. |
| 1212 | Structure nesting is too deep. |
| 1213 | CASE, ENDCASE, or OTHERWISE does not have corresponding DO CASE statement. |
| 1214 | ENDTEXT does not have corresponding TEXT statement. |
| 1217 | Picture error in GET statement. |
| 1220 | Command contains invalid character. |
| 1221 | Command is missing required clause. |
| 1223 | Invalid variable reference. |
| 1225 | Must be a memory or array variable. |
| 1226 | Must be a file variable. |
| 1229 | Too few arguments. |
| 1230 | Too many arguments. |
| 1231 | Missing operand. |
| 1232 | DIMENSION contains variable declaration without required subscript arguments. |
| 1234 | Subscript is outside defined range. |
| 1235 | Structure is invalid. |
| 1236 | Suspend program before using RESUME. |
| 1238 | No PARAMETER statement is found. |
| 1241 | Improper data type in the group expression. |
| 1242 | Syntax error in the field expression. |
| 1243 | Internal error: Too many characters in the report. |
| 1245 | Invalid expression in label definition file. |
| 1246 | Total label width exceeds allowed size. |
| 1249 | Too many READ commands are in effect. |
| 1250 | Too many PROCEDURE commands are in effect. |
| 1252 | Compiled code for this line is too long. |
| 1253 | Cannot rename the current directory. |
| 1254 | Cannot nest key labels. |
| 1255 | Key label «<label>» is invalid. |
| 1256 | Mismatched braces in key label. |
| 1257 | Key string is too long. |
| 1258 | Error in the PICTURE clause. |
| 1294 | FOXUSER.DBF file is invalid. |
| 1296 | Error reading the resource. |
| 1297 | Command is allowed only in interactive mode. |
| 1298 | «<name>» band is too large to fit on page. |
1300
| 1300 | Function name is missing ). |
| 1304 | Function name is missing (. |
| 1306 | Missing comma (,). |
| 1307 | Cannot divide by 0. |
| 1308 | Insufficient stack space. |
| 1309 | «<file>» is not an object file. |
| 1310 | Too many characters are specified in the PICTURE clause. |
| 1313 | Class creation canceled. |
| 1337 | Cannot nest the PRINTJOB command. |
| 1338 | File is the wrong version. |
1400
| 1405 | RUN|! command failed. |
| 1410 | Unable to create temporary work files. |
| 1411 | RUN|! command string is too long. |
| 1412 | Cannot locate the COMSPEC environment variable. |
| 1420 | OLE object is invalid or corrupted. |
| 1421 | Cannot activate the OLE server. |
| 1422 | Error saving the OLE object. |
| 1423 | Error creating the OLE object. |
| 1424 | Error copying the OLE object to Clipboard. |
| 1426 | OLE error code 0x»<name>». |
| 1427 | OLE IDispatch exception code «<name>». |
| 1428 | OLE IDispatch exception code %d from «<name>»: «<name>». |
| 1429 | «<name>». |
| 1431 | Maximum number of OLE arguments is exceeded. |
| 1434 | Class «<name>» has not been registered. |
| 1462 | «<name>» internal consistency error. |
| 1465 | SQL pass-through internal consistency error. |
| 1466 | Connection handle is invalid. |
| 1467 | Property is invalid for local Cursors. |
| 1468 | Property is invalid for a Cursor based on a table. |
| 1469 | Property value is out of bounds. |
| 1470 | Incorrect property name. |
| 1471 | Incorrect column format. |
| 1473 | Environment-level property is invalid. |
| 1474 | Invalid call issued while executing a SQLEXEC( ) sequence. |
| 1475 | Invalid call issued while executing a SQLMORERESULTS( ) sequence. |
| 1476 | Invalid call issued while executing a SQLTABLES( ) sequence. |
| 1477 | Invalid call issued while executing a SQLCOLUMNS( ) sequence. |
| 1478 | The ChildOrder property of a relation object in the data environment is no
longer valid. The relation is being removed. |
| 1479 | Invalid update column name «<name>». |
| 1480 | Warning: The connection sync/async mode cannot be set. |
| 1481 | Warning: The connection timeout cannot be set. |
| 1482 | Warning: The query timeout cannot be set. |
| 1483 | Warning: The connection packet size cannot be set. |
| 1484 | Warning: The next «<string>» modified records have already been updated
remote. |
| 1485 | Warning: Invalid data conversion(s) starting at record «<name>». |
| 1486 | Warning: Invalid memo field conversions starting at record «<name>». |
| 1487 | Warning: The connection transaction mode cannot be set. |
| 1490 | Saving a converted form as a class is not supported. |
| 1491 | No update tables are specified. Use the Tables property of the Cursor object. |
| 1492 | No key columns specified for the update table «<name>». Use the
«KeyFieldList» property of the Cursor object. |
| 1493 | SQL parameter is missing. |
| 1494 | View definition has been changed. |
| 1495 | Warning: The key defined by the KeyField property for table «<alias>» is not
unique. |
| 1496 | Warning: No information is available to check remote update conflicts. |
| 1497 | Connection name is invalid. |
| 1498 | SQL SELECT statement is invalid. |
| 1499 | SQL parameter «<name>» is invalid. |
1500
| 1502 | Cannot write to the record because it is in use. |
| 1503 | File cannot be locked. |
| 1507 | Screen code is too large for available memory. |
| 1508 | Error initializing OLE. |
| 1509 | Conversion canceled. Adjust the memo BLOCKSIZE. |
| 1510 | Invalid file format. If this is a dBASE file, it must be converted first. To
convert it, type MODIFY LABEL <labelfilename>. |
| 1520 | No database is open or set as the current database. |
| 1521 | This operation not supported for class member objects. |
| 1522 | Connectivity internal consistency error. |
| 1523 | Execution was canceled by the user. |
| 1524 | The selected printer driver does not support direct access. |
| 1525 | Function is not supported on remote tables. |
| 1526 | Connectivity error: «<name>». |
| 1527 | Cannot locate ODBC library, ODBC32.DLL. |
| 1528 | ODBC entry point missing, «<name>». |
| 1529 | File «<name>» already exists as part of a database. |
| 1530 | Fetching canceled; remote table is closed. |
| 1532 | Type conversion is not supported. |
| 1533 | This property is read-only. |
| 1534 | Database «<alias>» is not open. |
| 1535 | Cannot close this database because it is being used by a project container. |
| 1536 | Function is not supported on native tables. |
| 1538 | A stored procedure is executing. |
| 1539 | Trigger failed. |
| 1540 | Session number is invalid. |
| 1541 | Connection «<name>» is busy. |
| 1542 | Base table fields have been changed and no longer match view fields. View
field properties cannot be set. |
| 1543 | Type conversion required by the DataType property for field «<name>» is
invalid. |
| 1544 | DataType property for field «<name>» is invalid. |
| 1545 | Table buffer for alias «<name>» contains uncommitted changes. |
| 1546 | Cannot close table during execution of a table-bound expression. |
| 1547 | Cannot insert an empty row from a view into its base table(s). |
| 1548 | Table «<alias>» has one or more non-structural indexes open. Please close
them and retry the Begin Transaction. |
| 1549 | Data session #<number> cannot be released with open transaction(s). |
| 1550 | .DBC internal consistency error. |
| 1551 | File «<name>» is an invalid database. |
| 1552 | File «<name>» is not a database. |
| 1553 | File «<name>» is a database. |
| 1554 | Error instatiating cursor object. Table «<alias>» cannot be opened. Object
will be ignored. |
| 1557 | The database must be opened exclusively. |
| 1558 | File «<name>» is not part of a database. |
| 1559 | Property is not found. |
| 1560 | Invalid property value type. |
| 1561 | Database is invalid. Please validate. |
| 1562 | Cannot find object «<name>» in the database. |
| 1563 | Cannot find view «<name>» in the current database. |
| 1564 | Table «<name>» already exists in the database. |
| 1565 | File «<name>» is part of a database. |
| 1566 | Cannot issue the PACK command on a database while its tables are in use. |
| 1568 | View fields have been changed. The Update and Key field properties have been
reset to the default values. |
| 1569 | Database «<name>»: File access denied. |
| 1570 | Database is read-only. |
| 1571 | The name «<name>» is already used for another object. Please choose a
different name. |
| 1575 | Object name is invalid. |
| 1576 | Class «<name>» is not found in the class library. |
| 1577 | Table «<name>» is referenced in a relation. |
| 1578 | Invalid database table name. |
| 1579 | Command cannot be issued against a table with Cursors in table buffering mode. |
| 1580 | Feature is not supported for non-.DBC tables. |
| 1581 | Field «<name>» does not accept null values. |
| 1582 | Field «<name>» validation rule is violated. |
| 1583 | Record validation rule is violated. |
| 1584 | Error reading a property from the database. The property is ignored. |
| 1585 | Update conflict. Use TABLEUPDATE( ) with the lForce parameter to commit the
update or TABLEREVERT( ) to roll back the update. |
| 1586 | Function requires row or table buffering mode. |
| 1587 | Illegal nested OLDVAL( ) or CURVAL( ). |
| 1588 | INSERT cannot be issued when row or table buffering is enabled or when
integrity constraints are in effect. |
| 1589 | Table or row buffering requires that SET MULTILOCKS is set to ON. |
| 1590 | BEGIN TRANSACTION command failed. Nesting level is too deep. |
| 1591 | END TRANSACTION command cannot be issued without a corresponding BEGIN
TRANSACTION command. |
| 1592 | ROLLBACK command cannot be issued without a corresponding BEGIN TRANSACTION
command. |
| 1593 | Command cannot be issued within a transaction. |
| 1594 | Illegal to take file lock after record locks in transaction. |
| 1595 | Update conflict. Some of your changes in the current row batch were commited.
Use TABLEUPDATE( ) with the lForce parameter to commit the update or the manual transaction to roll back the update. |
| 1596 | Table buffering is not enabled. |
| 1597 | Views require either DB_BUFOPTROW or DB_BUFOPTTABLE. |
| 1598 | Rule and trigger code must balance transaction usage. |
| 1599 | Data session #<number> was forced to ROLLBACK back all transactions to avoid
deadlock. |
1600
| 1600 | Not enough memory to open a table with the USE command. |
| 1604 | No menu bar is defined. |
| 1605 | No menu is defined. |
| 1607 | Maximum menu items allowed (128) is exceeded. |
| 1608 | Maximum menus allowed (25) is exceeded. |
| 1609 | Maximum menu item length (50) is exceeded. |
| 1611 | Menu items and titles must be type Character. |
| 1612 | No such menu or menu item is defined. |
| 1621 | No menu titles have been defined for this menu. |
| 1632 | Window file format is invalid. |
| 1637 | File must be opened exclusively to convert the Memo file. |
| 1642 | Color set resource is not found. |
| 1643 | Printer driver is corrupted. |
| 1644 | Printer driver is not found. |
| 1645 | Report contains a nesting error. |
| 1646 | Total field type must be Date or Numeric. |
| 1647 | Field expression contains an invalid data type. |
| 1649 | No previous PRINTJOB command to correspond to this command. |
| 1651 | CANCEL or SUSPEND is not allowed. |
| 1652 | Invalid use of a Visual FoxPro function as an array. |
| 1653 | Label nesting error. |
| 1657 | Column number must be between 0 and the right margin. |
| 1659 | The table has memo fields that cannot be converted while open read-only. |
| 1661 | Microsoft Excel file format is invalid. |
| 1662 | Lotus 1-2-3 version 1.0 file format is invalid. |
| 1670 | Multiplan version 4.0 file format is invalid. |
| 1671 | Cannot import from password-protected file. |
| 1672 | Cannot append from password protected file. |
| 1673 | Symphony version 1.0 file format is invalid. |
| 1674 | Symphony version 1.1 file format is invalid. |
| 1678 | Lotus 1-2-3 version 3.0 file format is invalid. |
| 1679 | Import only Worksheet A for Lotus 1-2-3 version 3.0 files. |
| 1680 | Worksheet A for Lotus 1-2-3 version 3.0 file is hidden. |
| 1681 | PREVIEW clause is not allowed with OFF/NOCONSOLE or TO PRINT/FILE. |
| 1682 | Not a user-defined window. |
| 1683 | Index tag is not found. |
| 1684 | Index tag or file name must be specified. |
| 1685 | Project file is invalid. |
| 1686 | Form file «<name>» is invalid. |
| 1687 | Menu file is invalid. |
| 1688 | Paradox file format is invalid. |
| 1689 | Cannot build an .APP or .EXE file without a main program. |
| 1690 | Table operation is invalid during indexing. |
| 1691 | Library file is invalid. |
| 1692 | Unresolvable REGIONAL name conflict. |
| 1693 | Cannot find the menu-generation program. |
| 1694 | Too many extensions are specified. |
| 1695 | COLUMN | FORM | ALIAS | NOOVERWRITE | WIDTH are only allowed with the FROM
clause. |
| 1696 | NOWAIT | SAVE | NOENVIRONMENT | IN | WINDOW clauses are not allowed with the
FROM clause. |
| 1698 | COLUMN | ROW | ALIAS | NOOVERWRITE | SIZE | SCREEN are only allowed with the
FROM clause. |
1700
| 1705 | File access is denied. |
| 1706 | Cannot sort .IDX files in descending order. |
| 1707 | Structural .CDX file is not found. |
| 1708 | File is open in another work area. |
| 1710 | MULTISELECT or MOVER clause is not supported for PROMPT style menus. |
| 1711 | API library revision mismatch. Rebuild library. |
| 1712 | Field name is a duplicate or invalid. |
| 1713 | Field width or number of decimal places is invalid. |
| 1715 | Server «<server>» is not found. |
| 1716 | Queue «<queue>» is not found. |
| 1717 | Unable to generate a printer driver. |
| 1718 | File is read-only. |
| 1719 | Object file «<name>» is in use and cannot be removed from memory. |
| 1720 | Cannot issue the SET FORMAT command while a READ command is in progress on a
format file. |
| 1722 | Preprocessor expression is invalid. |
| 1723 | Mismatched #IF/#ELSIF/#ELSE/#ENDIF. |
| 1724 | Missing #ENDIF. |
| 1725 | Constant is already created with #DEFINE. |
| 1726 | API library is not found. |
| 1727 | A .DBF-style Help file is required. |
| 1728 | Color scheme is reserved. |
| 1731 | Cannot modify a base class. |
| 1732 | Data type is invalid for this property. |
| 1733 |
Class definition «<name>» is not found. |
| 1734 | Property «<name>» is not found. |
| 1735 | Error in the class definition. |
| 1736 | Error instantiating the object. |
| 1737 | «<name>» is a method, event, or object. |
| 1738 | Property «<name>» is not a method or event. |
| 1739 | Property setting will not take effect until data environment reloaded. |
| 1740 | «<name>» is a read-only property. |
| 1741 | Cannot add «<name>». Class definition is cyclical. |
| 1742 | The data source for this object must be a variable reference. |
| 1743 | Property «<name>» is read-only. |
| 1744 | Object class is invalid for this container. |
| 1746 | Class file name for «<name>» is not valid. |
| 1747 | Class file «<name>» is invalid. |
| 1749 | Form (.SCX) file must contain at least one Form. |
| 1750 | File version was created in a later version of Visual FoxPro than the current
version. |
| 1751 | Class file version is greater than the current version. |
| 1752 | Class is in use by MODIFY CLASS. |
| 1753 | Cannot load 32-bit DLL «<name>». |
| 1754 | Cannot find entry point «<name>» in the DLL. |
| 1755 | Cannot add an object to this class. |
| 1757 | Property «<name>» is protected. |
| 1758 | Cannot change property «<name>» in SetAll method. |
| 1759 | Expression is invalid. Use a valid expression for «<name>» property. |
| 1760 | Member «<name>» is a class member. |
| 1761 | Class «<name>» already exists. |
| 1762 | Class «<name>» is not found. |
| 1763 | Property «<name>» already exists. |
| 1764 | Array is not a member of the parent object. |
| 1765 | Method contains syntax errors. Method is not saved. |
| 1766 | Object «<name>» is not found. |
| 1767 | Parent object will not allow this property setting for «<name>». |
| 1768 | Cannot add this object to a Grid. |
| 1769 | Cannot add this object to a Column. |
| 1770 | Cannot clear a class that is in use. |
| 1771 | A member object with this name already exists. |
| 1773 | Database object type is invalid. |
| 1774 | No sheet is found. |
| 1775 | Worksheet «<name>» is not found. |
| 1776 | Class «<name>» is in use. |
| 1777 | Cannot modify the structure of a view cursor. |
| 1778 | The table must be converted before appending. |
| 1779 | Error adding «<name>» to the object. Duplicate member/property name. |
| 1780 | This array element has been defined as an object and cannot be redefined in
the class definition. |
| 1781 | An objects control source cannot be set to its Value property. |
| 1782 | This OLE property cannot be an expression. |
| 1795 | Cannot arrange an untransported form. |
| 1796 | No records are found for the current platform. |
| 1797 | Error occurred in conversion. |
| 1798 | Cannot find the converter program. |
1800
| 1800 | SQL: Internal error. |
| 1801 | SQL: Error correlating fields. |
| 1802 | SQL: Cannot locate table. |
| 1803 | SQL: HAVING clause is invalid. |
| 1804 | SQL: Statement is invalid. |
| 1805 | SQL: Too many subqueries. |
| 1806 | SQL: Column «<field> | <variable>» is not found. |
| 1807 | SQL: GROUP BY clause is invalid. |
| 1808 | SQL: ORDER BY clause is invalid. |
| 1809 | SQL: Out of memory. |
| 1810 | SQL: Invalid use of subquery. |
| 1811 | SQL: Aggregate on non-numeric expression. |
| 1812 | SQL: Statement too long. |
| 1813 | SQL: Use of UNION in subquery is invalid. |
| 1814 | SQL: Queries of this type are not supported. |
| 1815 | «<cursor>» must be created with SELECT … INTO TABLE. |
| 1818 | SQL: FROM clause is required. |
| 1819 | SQL: DISTINCT is invalid. |
| 1820 | SQL: SELECT contains invalid *. |
| 1822 | SQL: Invalid aggregate field. |
| 1825 | SQL: Subquery is invalid. |
| 1826 | SQL: SELECT is invalid. |
| 1828 | SQL: Illegal GROUP BY in subquery. |
| 1830 | SQL: Index is not found. |
| 1831 | SQL: Error building temporary index. |
| 1832 | «<field> | <variable>» is not unique and must be qualified. |
| 1833 | SQL: WHERE clause is invalid. |
| 1834 | SQL: Too many UNIONs. |
| 1839 | SQL: Operation was canceled. |
| 1841 | SQL: Too many columns referenced. |
| 1842 | SQL: Subquery nesting is too deep. |
| 1844 | Cannot nest aggregate functions. |
| 1845 | SQL expression is too complex. |
| 1846 | Cannot GROUP by aggregate field. |
| 1851 | SELECTs are not UNION compatible. |
| 1860 | Subquery returned more than one record. |
| 1864 | SQL: Too many fields in UPDATE. |
| 1865 | SQL: Invalid SET expression in UPDATE. |
| 1870 | ALTER TABLE operation interrupted. |
| 1871 | Cannot DROP all existing columns. |
| 1872 | Too many columns. |
| 1875 | Too many key changes defined. |
| 1877 | No rule to DROP. |
| 1878 | No default value to DROP. |
| 1879 | No primary key. |
| 1880 | Related table is not found in current database. |
| 1881 | Error loading: «<error>». |
| 1882 | Related tag is not found or not primary or candidate. |
| 1883 | Primary key already exists. |
| 1884 | Uniqueness of index «<name>» is violated. |
| 1885 | Only structural tags can be defined as candidate. |
| 1886 | Index does not accept NULL. |
| 1887 | Illegal recursion in rule evaluation. |
| 1888 | Tag name is too long. |
| 1889 | A table in multiple relationships can only have one child order. |
1900
| 1903 | String is too long to fit. |
| 1907 | Drive specifier is invalid. |
| 1908 | Length or decimal place argument is invalid. |
| 1910 | Illegal printer driver recursion. |
| 1911 | Localized product is required for this environment. |
| 1912 | Operation is invalid for a General field. |
| 1913 | Field must be a General field. |
| 1914 | Code page number is invalid. |
| 1915 | Collating sequence «<name>» is not found. |
| 1916 | To use this feature, set CODEPAGE=AUTO in your CONFIG.FPW file and restart
Visual FoxPro. |
| 1918 | File name is too long. |
| 1922 | Volume does not exist. |
| 1923 | Object «<name>» is not found. |
| 1924 | «<name>» is not an object. |
| 1925 | Unknown member «<name>». |
| 1926 | Cannot nest class definitions. |
| 1927 | Statement is not valid in a class definition. |
| 1928 | Statement is only valid within a class definition. |
| 1929 | «<name>» can only be used within a method. |
| 1930 | Cannot redefine «<name>». |
| 1931 | Statement is not in a procedure. |
| 1932 | «<name>» cannot be closed while suspended. |
| 1933 | File «<name>» is not closed. |
| 1934 | Statement only valid within a method. |
| 1935 | The current object does not inherit from class «<name>». |
| 1937 | Procedure file «<name>» is not found. |
| 1938 | Object is not contained in a «<name>». |
| 1939 | WITH/ENDWITH mismatch. |
| 1940 | Expression is not valid outside of WITH/ENDWITH. |
| 1942 | Objects cannot be assigned to arrays. |
| 1943 | Member «<name>» does not evaluate to an object. |
| 1944 | Cannot find the builder program. |
| 1945 | The current object has been released. |
| 1947 | Expression is too complex. |
| 1948 | Cannot modify a class that is in use. |
| 1949 | Class name is invalid. |
| 1950 | Class definition «<name>» is recursive. |
| 1951 | Cannot clear the object in use. |
| 1953 | Feature is only available if the object is in design mode. |
| 1954 | The builder program is already running. |
| 1955 | WIN.INI/registry is corrupted. |
| 1956 | Printer access denied. |
| 1957 | Error accessing printer spooler. |
| 1958 | Error loading printer driver. |
| 1960 | Illegal redefinition of variable «<name>». |
| 1961 | A subdirectory or file «<name>» already exists. |
| 1962 | The directory is not empty. |
| 1963 | Directory is not found. |
| 1964 | No PageFrame or Page is found in FormSet or Form with WindowType set to READ
or READ MODAL. |
| 1965 | One of the members of this class is based on a nonvisual class. Cannot write
.VCX file. |
| 1966 | Data environment is already loaded. |
| 1967 | Data environment is already unloaded. |
| 1968 | One of the members of this Form or FormSet is based on a nonvisual class.
Cannot write .SCX. |
| 1969 | The clipboard contained one or more objects that could not be added to the
container. Those objects were not pasted. |
| 1970 | Cannot reopen project file «<name>». |
| 1971 | Cannot compile until the current COMPILE command has completed. |
| 1972 | Array «<name>» is in use. |
| 1973 | Error creating table: «<name>». |
| 1974 | Arrays cannot be assigned to array elements. |
| 1975 | Member object «<name>» has not been instantiated. |
| 1976 | Cannot resolve back link. |
| 1977 | Exclusive open required to update this file. Please reopen. |
| 1978 | Cannot visually modify a class of this type. |
| 1979 | Cannot visually modify a class based on a nonvisual class. |
| 1981 | Compile error in file «<name>». |
| 1982 | The TO clause can only be used with modal Forms and FormSets. |
| 1983 | «<name>» objects cannot return a value. |
| 1984 | The fields in table «<name>» did not match the entries in the database. |
| 1985 | Object already has a data environment, cannot use another one with the SaveAs
method. |
| 1986 | GDI memory is low, close one or more windows and try again. |
| 1987 | Objects cannot be programmatically added to the Form Designers FormSet. |
| 1988 | Currency value is out of range. |
| 1989 | Objects cannot be programmatically deleted from the Form Designer. |
| 1991 | Form file «<name>» is from a previous version of FoxPro. It must be converted
to the current format before it can be executed. |
| 1993 | Class library file «<name>» is not found. |
| 1994 | Include file «<name>» is not found. |
| 1995 | Error loading the data environment. Table is in use. |
| 1996 | The validation rule for field «<name>» does not evaluate to a logical or NULL
type. |
| 1997 | The default value for field «<name>» does not evaluate to the correct type for
the field. |
| 1998 | The index expression for index «<name>» contains an invalid reference to a
Memo, General, or Picture field. |
| 1999 | Function is not implemented. |
With the introduction of Visual FoxPro 3.0, error handling in VFP changed substantially.
Rather than using «on error» statements, «state of the art» error events became available. Now, 7 years later, more sophisticated error handling mechanisms take center stage as Visual FoxPro 8.0 introduces structured error handling.
Handling potential errors in the most graceful way has been a goal of software developers since the very early days of programming, and the quest for the perfect methodology is still ongoing. FoxPro and Visual FoxPro have gone through a number of different ways to handle errors (all of which are still available today and are useful for different scenarios).
The «most traditional» way to handle errors in FoxPro (and even in FoxBase, before) was the ON ERROR statement. This command tells FoxPro what to do in case of an error. Arguably one of the most common scenarios would be to call a procedure that handles an error in the following fashion:
ON ERROR DO ErrorHandler
Each Try-block needs to have at least a CATCH or a FINALLY block.
Or, you might use a slightly more sophisticated version, as suggested by the Visual FoxPro documentation:
ON ERROR DO errhand WITH ;
ERROR( ), MESSAGE( ), MESSAGE(1),;
PROGRAM( ), LINENO( )
Of course, in the object-oriented world that Visual FoxPro lives in, this is a very procedural way to handle things. Luckily, the ON ERROR command can evaluate any Visual FoxPro expression, including calling methods on an object:
ON ERROR oErrorHandler.Handle()
This approach works rather well in scenarios where a global error handler is used. However, this type of error handler is generally not used in an object-oriented environment. There are a number of reasons for this. First of all, in order to create black-box objects, those objects have to handle their own errors to conform to the rules of object-oriented development. However, to make those objects handle their own errors, we would have to set up an error handler like so:
ON ERROR THIS.HandleErrors()
Unfortunately, this doesn’t work, because whatever is specified as the ON ERROR statement will run as if it were a separate procedure. In other words, this line of code will run outside the object. Therefore, the THIS pointer is not valid.
Another issue is that the ON ERROR statement would not be scoped to the object. Consider the following example:
ON ERROR * && Ignore errors
LOCAL loExample
loExample = CREATEOBJECT("Example")
xxxxxxx && Syntax error
RETURN
DEFINE CLASS Example AS Custom
FUNCTION Init
ON ERROR THIS.HandleErrors
RETURN .T.
ENDFUNC
FUNCTION HandleErrors
MESSAGEBOX("Handling Errors...")
ENDFUNC
ENDDEFINE
In this particular example, the first line instructs VFP to ignore all errors (the error statement is an asterisk, which is a comment line). Then, the Example object gets instantiated, and its constructor (Init()), sets the error statement to «THIS.HandleErrors» (for now, let’s just assume that would be a valid line of code). After the object is instantiated, a line of code with a syntax error («xxxxxxx») executes, raising an error.
The question is: What error handler will handle that error? Since we now know that ON ERROR is not scoped to objects, we also know that the error is handled by «THIS.HandleErrors». Clearly, even if that would call a method on the right object, this wouldn’t be desired, since the object has no knowledge about how to handle errors that may occur outside the object. Similarly, error handlers defined after the object is instantiated would throw off error handling within the object. Neither scenario will allow us to create black box objects.
One possible solution would be to create an object devoted to error handling. This object could be created when the main object gets instantiated. However, to make this work, the new object would have to be referenced through a public variable so it could be referenced (again, THIS.oError.HandleErrors() would not work). This could lead to collisions with other objects that employ the same approach. Also, each individual method would have to set the error handler to that handler object, and reset it back not only when the method completed, but also every time the object called out to other code (which may or may not use a similar approach). This certainly would be an error-prone solution. Let’s not even investigate it any more, although I could point out a long list of other problems.
Clearly, a better way to handle errors was required. For this reason, Visual FoxPro 3.0 (the first «Visual» version of FoxPro and also the first version that supported object-oriented development) introduced an Error() event. Using that mechanism, errors could be handled in the following fashion:
ON ERROR * && Ignore errors
LOCAL loExample
loExample = CREATEOBJECT("Example2")
xxxxxxx && Syntax error
RETURN
DEFINE CLASS Example2 AS CUSTOM
PROCEDURE INIT
xxxxx && Syntax error
ENDPROC
PROCEDURE ERROR(nError, cMethod, nLine)
MESSAGEBOX("Error inside the object")
ENDPROC
ENDDEFINE
The idea here is simple: Whenever an error occurs anywhere within the object, the Error() event will fire. If the error occurred anywhere outside the object, it will be handled by whatever error handler is defined there. In our example, the syntax error in the Init() method will be handled by the Error() method, and the syntax error in the line of code after the CreateObject() will be handled by the ON ERROR error handler (which will actually hide the error).
This mechanism has a number of advantages. First of all, it allows building self-contained objects. Secondly, it splits the gigantic task of handling errors globally, into smaller, more digestible pieces. No longer are we dealing with handling a very large number of errors. For instance, if the object at hand doesn’t deal with database tables, we probably don’t have to worry about handling any database errors.
However, this approach also has some problems. For example, it still may be handling errors on a scale much larger than we want. Objects can be large and do a large number of different things, each of which may have only a very limited number of scenarios that may go wrong. In total, however, the object might require a very complex error handler.
Another problem is that this type of error handler makes it very difficult to «exit gracefully» whenever an error has occurred. Consider the following example:
DEFINE CLASS WordExport AS Custom
FUNCTION Export(lcText1,lcText2)
LOCAL oWord as Word.Application
oWord = CREATEOBJECT("Word.Application")
oWord.Application.Visible = .T.
oWord.Documents.Add()
oWord.Selection.InsertAfter(lcText1)
oWord.Selection.InsertAfter(lcText2)
RETURN .T.
ENDFUNC
PROCEDURE ERROR(nError, cMethod, nLine)
MESSAGEBOX("Error exporting to Word!")
ENDPROC
ENDDEFINE
The idea behind this simplified example is that the WordExport object can be used to create a Word document on the fly. To do so, the developer simply instantiates this object and passes some text to the Export() method. The method then opens an instance of Word, makes it visible, creates a new document and exports the text.
What would happen if the user actually closed Word right after a new document has been created (right after the Documents.Add() line)? Well, the next two lines of code would both cause an error (and so would hundreds of other lines if this was a life-size example).
But what could our error handler do to solve the problem? Well, beyond displaying the error in a message box, the error handler could try to fix the problem. However, this is unlikely in this case, because in order to do that, the method would have to start over from scratch. Since that isn’t something the error handler could do easily, it can choose to ignore the error and proceed with the next line of code, which would then cause another error that could also be ignored, and so forth.
Another option would be to issue a RETRY, which would run the line that failed again, causing another error, which would result in an endless loop if the handler just tried to RETRY again. The only other option we have would be to CANCEL, which would shut down the whole process and not just the current method.
Note also, that the method returns .T., which is the way I would like things to be if the document got created successfully. However, I would like the method to return .F. if there was a problem. This isn’t so easy, since the Error() event doesn’t have any access to the return value of this method.
One possible solution would be a local ON ERROR statement instead of the error method:
DEFINE CLASS WordExport AS Custom
FUNCTION Export(lcText1,lcText2)
* We create a new error handler
LOCAL lError
lError = .F.
ON ERROR lError = .T.
* We run the regular code
LOCAL oWord as Word.Application
oWord = CREATEOBJECT("Word.Application")
oWord.Application.Visible = .T.
oWord.Documents.Add()
oWord.Selection.InsertAfter(lcText1)
oWord.Selection.InsertAfter(lcText2)
* We reset the error handler,
* and check if everything went fine
ON ERROR
IF lError
RETURN .F.
ELSE
RETURN .T.
ENDIF
ENDFUNC
ENDDEFINE
This is an acceptable solution, but there are difficulties with this approach. First of all, the method might call out to other methods that may reset the error handler or point to a different handler. This is a problem that is hard to avoid, since you may not have control over other code that is running.
Also, at a later point in time, someone may want to add an Error() method to this object (perhaps to handle errors that may occur in other methods). The problem with that is that the error method takes precedence over the ON ERROR handler, hence rendering the ON ERROR useless.
Introducing: Try/Catch
To solve these issues, Visual FoxPro 8.0 introduces «Structured Error Handling.» This approach allows the developer to wrap a series of commands into a block that is handled by a local error handler. The advantage of this error handler is that it usually handles a very limited set of potential problems, making it simple and straightforward.
This is the basic syntax for structured error handling in Visual FoxPro:
TRY
* Do something
CATCH
* Handle a potential problem
ENDTRY
Let’s see how we could re-work the above example into a scenario handled by a Try/Catch block:
DEFINE CLASS WordExport AS Custom
FUNCTION Export(lcText1,lcText2)
LOCAL lReturnValue
lReturnValue = .T.
TRY
* We run the regular code
LOCAL oWord as Word.Application
oWord = CREATEOBJECT("Word.Application")
oWord.Application.Visible = .T.
oWord.Documents.Add()
oWord.Selection.InsertAfter(lcText1)
oWord.Selection.InsertAfter(lcText2)
CATCH
lReturnValue = .F.
ENDTRY
RETURN lReturnValue
ENDFUNC
ENDDEFINE
As we can see, this is a much simpler way to implement the solution. First of all, it is simply much less «kludgy» and is a very clean implementation. But more importantly, it is a much superior implementation from a technical point of view. The solution is not influenced by outside error handling.
Also, we have full control over what is to happen if an error does occur. Unlike in the example with the error event, we can write code within our method that executes no matter whether an error occurred or not, making it easy to set the return value to our liking. (We were able to do this in the previous example, but the solution was error prone and easy to break by running it in different environments).
Try/Catch blocks can be nested to achieve more granular error handling.
I’m sure that by now you already have a good idea about what Try/Catch does: Whatever code we run inside a Try-block will execute until an error occurs. If an error does in fact occur, the Catch-block is executed. Note that the try block stops executing as soon as an error occurs. There is no way to retry or ignore the error. If that’s what you would like to do, Try/Catch error handling is not the right solution.
Note that the Catch-block is never executed if no error occurs. Sometimes you might want to define code that runs as cleanup code, whether an error occurred or not. Here is an example:
DEFINE CLASS WordExport AS Custom
FUNCTION Export(lcText1,lcText2)
LOCAL lReturnValue
lReturnValue = .T.
TRY
* We run the regular code
LOCAL oWord as Word.Application
oWord = CREATEOBJECT("Word.Application")
oWord.Application.Visible = .T.
oWord.Documents.Add()
oWord.Selection.InsertAfter(lcText1)
oWord.Selection.InsertAfter(lcText2)
CATCH
lReturnValue = .F.
FINALLY
IF VarType(oWord) = "O"
oWord.Application.Quit()
ENDIF
ENDTRY
RETURN lReturnValue
ENDFUNC
ENDDEFINE
In this example, we shut down Word, even if something went wrong. Note however, that the error may have occurred before Word ever got instantiated. Therefore we need to first check whether Word is an object. (Actually, things may be a little trickier with automation objects, especially Word, but for simplicity we’ll leave it at that.)
At this point you may wonder why we need a Finally-block. After all, we could have put that code after the ENDTRY and would have achieved an identical result. However, there are scenarios that can greatly benefit from using the finally-block (which we will examine further down), making the use of FINALLY a good idea in general.
One last remark about the basic Try/Catch structure: Each Try-block needs to have at least a CATCH or a FINALLY block. Therefore, you can not just say «try this, and I don’t care of it works or not since I can’t do anything about a potential problem anyway.» If you would like to do that, you can create a Catch-block that has nothing but a comment. A scenario like this may be desired within an error handler:
TRY
USE Customer
LOCATE FOR LastName = "Gates"
IF FOUND()
StrToFile("Gates found!","customer.log")
ENDIF
CATCH
TRY
StrToFile("Error: "+Message(),"Error.log")
CATCH
* Nothing we can do now
ENDTRY
FINALLY
IF Used("Customer")
USE IN Customer
ENDIF
ENDTRY
This example also demonstrates one of the key features of structured error handling: Nested Try/Catch blocks.
Nested Try/Catch Blocks
Try/Catch blocks can be nested to achieve more granular error handling. There may be a Try/Catch block around the entire application, there may be Try/Catch blocks wrapping entire methods, then there may be individual blocks, and so forth.
Let’s enhance our Word example a little more and instead of creating a blank document, we will create a new one based on a certain template:
FUNCTION Export(lcText1,lcText2)
LOCAL lReturnValue
lReturnValue = .T.
TRY
* We run the regular code
LOCAL oWord as Word.Application
oWord = CREATEOBJECT("Word.Application")
oWord.Application.Visible = .T.
TRY
oWord.Documents.Add("MyTemplate.dot")
CATCH
oWord.Documents.Add()
ENDTRY
oWord.Selection.InsertAfter(lcText1)
oWord.Selection.InsertAfter(lcText2)
CATCH
lReturnValue = .F.
ENDTRY
RETURN lReturnValue
ENDFUNC
In this example, the inner Try/Catch block traps only errors that may occur while a new document is created based on the specified template. Presumably, if that template doesn’t exist, an error will be raised and caught by the Catch-block, which will create a blank document. The code then proceeds as planned.
Note that the Catch-block may raise another error that will then be handled by the «outer» Catch-block (which simply sets the return value and gives up).
There is one potential problem here. We are assuming that the error has been caused by the fact that the template doesn’t exist. But of course, there could be a number of other scenarios causing other problems. For instance, the problem could be caused by the user closing Word right after it became visible (yes, they’d have to be very quick, but hey, this is only an example!). In our little example, this wouldn’t be a problem. Worst case, the Catch-block fails again and defaults to the outer handler, which will handle the situation appropriately. However, in many complex scenarios, we would have to look at additional error information and handle the situation appropriately.
Conditional Error Handling
Visual FoxPro has a number of functions to retrieve error information, such as Message(). However, those functions are not really adequate to make this bullet-proof, since nested errors make things a bit complicated. For this reason, Microsoft introduced an Exception object. The exception object can be invoked simply by using it on the CATCH statement:
CATCH TO oException
This will make an object named «oException» available within the Catch-block. This object has a number of properties, such as ErrorNo, Message, LineNo, Details, LineContents, and more. Using this construct, we can use the following syntax to check for errors caused by the template only:
FUNCTION Export(lcText1,lcText2)
LOCAL lReturnValue
lReturnValue = .T.
TRY
* We run the regular code
LOCAL oWord as Word.Application
oWord = CREATEOBJECT("Word.Application")
oWord.Application.Visible = .T.
TRY
oWord.Documents.Add("MyTemplate.dot")
CATCH TO oException
IF oException.ErrorNo = 1429
oWord.Documents.Add()
ELSE
* We have a different problem
THROW oException
ENDIF
ENDTRY
oWord.Selection.InsertAfter(lcText1)
oWord.Selection.InsertAfter(lcText2)
CATCH
lReturnValue = .F.
ENDTRY
RETURN lReturnValue
ENDFUNC
In this example, we handle only error 1429, which is the one that is raised if the template wasn’t there. The question is: What do we do with all other errors? Well, basically, we want it to be handled the same way all other errors are handled within the outer Try-block. Therefore, we need to elevate the error to that level. We can do so using the THROW statement. This will «re-throw» the error, causing it to be handled by the outer Catch-block. (Exceptions elevated using a THROW statement will end up as user exceptions in the outer error handler. See below for more information.)
The WHEN clause of the CATCH statement can utilize any valid Visual FoxPro expression.
This is a pretty simple example. All we really check for is the error number. But, imagine we check for other conditions. For instance, we could try to find another template, or download it from somewhere, and so forth. If all of those attempts fail, we would re-throw the error. If all we wanted to check was the error number, though, we could do something even simpler:
TRY
oWord.Documents.Add("MyTemplate.dot")
CATCH TO oEx WHEN oEx.ErrorNo = 1429
oWord.Documents.Add()
ENDTRY
This will catch only error 1429. All other errors will be automatically elevated to the outer error handler, if there is one. Otherwise, the default VFP error dialog would be shown. Therefore, this is a shortcut that is functionally identical to the version shown in the previous example (except that the exception elevated to the outer handler will not be a user error).
What makes this feature very powerful is that there can be a number of different catch-blocks:
TRY
oWord.Documents.Add("MyTemplate.dot")
CATCH TO oEx WHEN oEx.ErrorNo = 1429
oWord.Documents.Add("MyOtherTemplate.doc")
CATCH TO oEx WHEN oEx.ErrorNo = 1943
MessageBox("Stop closing Word!!!")
CATCH
MessageBox("Something else happened!")
ENDTRY
Note that catch blocks are evaluated from top to bottom, and only one of them will run. Therefore, the chosen sequence is important. If we change this example to the following, we would see unexpected (or «expected» after you read this article) results:
TRY
oWord.Documents.Add("MyTemplate.dot")
CATCH
MessageBox("Something else happened!")
CATCH TO oEx WHEN oEx.ErrorNo = 1429
oWord.Documents.Add("MyOtherTemplate.doc")
CATCH TO oEx WHEN oEx.ErrorNo = 1943
MessageBox("Stop closing Word!!!")
ENDTRY
In this scenario, only the first catch-block will ever be executed, because it is so generic, it will catch all the errors and the subsequent catch statements will never be evaluated.
The WHEN clause of the CATCH statement can utilize any valid Visual FoxPro expression. Note, however, that to avoid having an erroneous catch statement you shouldn’t make these statements too complex.
Throwing Custom Errors
As we have seen in previous examples, the new THROW command can be used to elevate errors the error handler chooses not to handle, so an outer error handler (perhaps another Catch-block, or some other type of error handler) can attempt to handle the error. What’s not as obvious is that THROW can be used to raise custom errors, allowing us to architect our applications in an entirely different fashion.
Listing 1 shows an example for this technique. In this example, we have a class called CreditCard that simulates a credit card charging object. This object is rather simple. All it has is one method called ChargeCard(), and all that method does is check if the passed credit card number is «12345678». If so, the card is considered valid. This is a simplistic example, but all we are really interested in is the error handling. So let’s see what happens when the card number is invalid.
First of all, the ChargeCard() method instantiates a class called CreditCardException and passes some detailed error information to its constructor. This class is defined a little further down and is a simple subclass of the new Visual FoxPro Exception base class. It has a few overridden properties, and one additional one that gets set based on the value passed to the constructor. Once that object is instantiated, the CreditCard class raises an error (exception) using the THROW command and the new exception object as the expression. This will immediately halt the execution of the ChargeCard() method, and invoke whatever error handler is currently in use.
What’s not as obvious is that THROW can be used to raise custom errors, allowing us to architect our applications in an entirely different fashion.
So now let’s work our way back up towards the beginning of this listing to see how this code is invoked. The listing starts out with the instantiation of the credit card object and a call to the ChargeCard() method. The parameter passed to this method represents an invalid credit card (error handling is easier to demonstrate if things fail). All of this is wrapped into a Try/Catch block.
Note that the Catch-block traps for error 2071. All user-thrown exceptions end up as error 2071. In this particular example, those are all the errors we are really interested in. Of course, there could be other errors occurring, and those are caught by the second Catch-block. In a larger example, there could also be an outer error handler so we wouldn’t have to worry about that possibility. The second Catch-block is not required and I just included it because I’d consider it «good form.»
So what exactly happens when a user-thrown error occurs and our Catch-block kicks in? Well, first of all, there could be a number of different user-thrown errors, and we are not interested in any of them other than our custom exception. The user defined information is stored in a property called UserValue, which is a variant and could be anything. In our case, it is another exception object, since that’s what we threw, but it could be a string or any other value if the exception was thrown in the following manner:
THROW "Something is wrong!"
Since we threw an object, we can now check for detailed information on that object, such as the error number or perhaps even the class. If we discover error number 10001 (which is our custom error number), we can handle it. Otherwise, it is a different user-thrown error, and we really do not know what to do at all, so we simply elevate the error to the next level by re-throwing it.
Note that this example is not bullet-proof. The following line of code may, in fact, cause other errors:
IF oEx.UserValue.ErrorNo = 10001
If UserValue is not an object, or if it is an object but doesn’t have a property called ErrorNo, this would result in yet another exception, which would be thrown to an outer exception handler. Note that the outer exception handler would receive a FoxPro error, and not the user thrown error, which would not be a good thing at all.
At this point, you may wonder how UserValue could be an object but not have that property. The reason is simple: Just like one can throw a string or a number as the user value, one could throw any type of object as the user value. The thrown object doesn’t have to be subclassed from Exception.
One of the «gotchas» with this type of architecture is that youi should really use Try/Catch blocks to catch these user thrown errors. Technically, you can use ON ERROR to catch our CreditCardException, but it is a bit trickier to do so since no error object is available.
One last word of caution: The use of a THROW statement will always end up as a user thrown error. This means that if you intend to elevate an error from within a catch block to an outer error handler, you may be re-throwing a system error, but it will end up as a user error in the next-level error handler. The original (system) exception object will end up as the UserValue. Of course, to handle these situations correctly, the outer exception handler needs to be aware of this.
Mixing Error Handling Methodologies
Structured error handling is great and will replace traditional error handling in most scenarios. In fact, some modern languages like C# have only structured error handling. However, there are some downsides to structured error handling, such as no intrinsic retry capability. Also, in many scenarios, pre-existing, non-structured error handling may be in place.
So let’s look at a few examples of mixed error handling and the effects it may have on your code. Let’s start out with a simple one:
TRY
ON ERROR MESSAGEBOX(MESSAGE())
xxxxx
ON ERROR
xxxxx
CATCH
MESSAGEBOX("Exception!")
ENDTRY
In this example, we define an ON ERROR statement within a Try/Catch block («xxxxx» always represents some kind of error in these examples). What would we expect to happen here?
Most people I present this to would expect the ON ERROR to handle the first problem, and the Catch-block to handle the second error. This is not the case! The Catch-block takes precedence over the ON ERROR and handles both exceptions.
At this point, you may wonder why one would ever define an ON ERROR inside a Try/Catch. In real-world environments, this is a rather common scenario. Consider this example:
TRY
ErrTest()
CATCH
MESSAGEBOX("Exception!")
ENDTRY
FUNCTION ErrTest
ON ERROR MESSAGEBOX(MESSAGE())
xxxxx
ENDFUNC
The Try/Catch wraps a simple call to another function (or method). That function apparently has its own error handling using the old ON ERROR methodology. However, the local error handling mechanism used by that function is now taken hostage by our Catch-block. As you can imagine, this may result in some surprising behavior.
We can produce a similar example using the Error() method:
TRY
oTest = CREATEOBJECT("TestClass")
oTest.Execute()
CATCH
MESSAGEBOX("Exception!")
ENDTRY
DEFINE CLASS TestClass AS Custom
FUNCTION Execute
xxxxxx
ENDFUNC
FUNCTION Error(nError, cMethod, nLine)
MESSAGEBOX(MESSAGE())
ENDFUNC
ENDDEFINE
In this example, we are also calling another method that has a local error handler. However, this time the result is opposite from the previous example. The Error() event takes precedence over the Try/Catch and handles the error inside the called object.
So what would happen if we added some structured error handling to the TestClass object?
DEFINE CLASS TestClass AS Custom
FUNCTION Execute
TRY
xxxxxx
CATCH
MESSAGEBOX("Exception 2!")
ENDTRY
ENDFUNC
FUNCTION Error(nError, cMethod, nLine)
MESSAGEBOX(MESSAGE())
ENDFUNC
ENDDEFINE
In this example, the new Try/Catch will handle the error since it has been defined at a higher level of granularity.
An interesting question here is, «What happens if that Catch-block re-throws the error?»
DEFINE CLASS TestClass AS Custom
FUNCTION Execute
TRY
xxxxxx
CATCH TO oEx
MESSAGEBOX("Error!")
THROW oEx
ENDTRY
ENDFUNC
FUNCTION Error(nError, cMethod, nLine)
MESSAGEBOX(MESSAGE())
ENDFUNC
ENDDEFINE
In this example, the Error() method will get a chance to handle the re-thrown error. The outer error handler will not have the opportunity to handle the exception, because it is not possible to elevate the error from within the Error() method because the exception object is not available there. The only option would be to throw a custom error.
Finally
I still owe you an explanation of the FINALLY statement. In many scenarios, it may seem as if FINALLY may not really be required, since the flow of the program is likely to continue after the Try/Catch section. «Likely» is the key term here. If a potential error is not handled in a Catch-block (either because there isn’t a matching Catch-block or because another exception is THROWn), code after the Try/Catch statements may not be executed at all. Consider this example:
DEFINE CLASS TestClass AS Custom
FUNCTION Execute
TRY
xxxxxx
CATCH TO oEx
MESSAGEBOX("Error!")
THROW oEx
FINALLY
MESSAGEBOX("Cleanup Code")
ENDTRY
MESSAGEBOX("More Code")
ENDFUNC
ENDDEFINE
In this example, the syntax error in the Try-block is caught by the Catch-block, just to be re-thrown again. This means that the very last MessageBox() will never be executed. However, the MessageBox() in the Finally-block will be executed in every case, even if no exception occurred.
Conclusion
Structured Error Handling is one of the most important language enhancements Visual FoxPro has seen in a while. It is very powerful and helps you tremendously in your attempts to produce bullet-proof code.
If you have any questions about this technology, feel free to email me.