Меню

Nesting error foxpro ошибка

  • Remove From My Forums
  • Question

  • FOR XX = 1 TO 15 STEP 1
    *
    *
    *
    NEXT XX && <— Error is here rarely

    Using 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 xx

    What’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

  • 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

  • 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

  • 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

  • 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

  • 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

Содержание

  1. Nesting error in foxpro
  2. Nesting error in foxpro
  3. Nesting error in foxpro
  4. Answered by:
  5. Question
  6. Answers
  7. Nesting error in foxpro
  8. Answered by:
  9. Question
  10. Answers
  11. 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
Дата регистрации: 28.05.2002

Re: Что означает ошибка Nesting error?
Dutch

Сообщений: 212
Откуда: Волгодонск
Дата регистрации: 25.10.2010

Жесть. Назачем тогда вообще это меню надо

Источник

Nesting error in foxpro

Вот спасибо! Не знал об этом.

Запускаю форму так:

Для удобства, решил что имя файла, свойство Form.NAME и имя объектной переменной будут иметь одинаковое значение. В конкретном случае это ‘Spr_Unit’. Насчёт более чем одного запуска формы — логикой программы эта возможност исключена.

Грид лежит на форме, которая является экземпляром пользовательсого класса, наследника класса Form. И он удачно «находится».

Исправлено 3 раз(а). Последнее : Dutch, 08.11.12 15:59

Re: Что означает ошибка Nesting error?
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002

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
Откуда: Волгодонск
Дата регистрации: 25.10.2010

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
Дата регистрации: 29.07.2002

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

Re: Что означает ошибка Nesting error?
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002

Re: Что означает ошибка Nesting error?
XAndy

Сообщений: 3803
Откуда: Киев
Дата регистрации: 05.02.2004

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
Дата регистрации: 28.05.2002

Re: Что означает ошибка Nesting error?
Dutch
Автор

Сообщений: 212
Откуда: Волгодонск
Дата регистрации: 25.10.2010

Жесть. Назачем тогда вообще это меню надо

Источник

Re: Что означает ошибка Nesting error?
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002

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.

fox00017.gif

***

*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.

0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

А вот еще интересные материалы:

  • Яшка сломя голову остановился исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного где ошибка
  • Nero ошибка стирания ошибка калибровки мощности
  • Nero ошибка процесса записи