|
||
Пособие-самоучитель |
||
Глава 11. Перехват и обработка ошибок. Операторы On Error и Resume. Объект Err. Оператор GoTo. |
||
Скачать |
Дата создания 11.01.2005 {Автор 4us} |
|
Как бы правильно не был бы написан код программы и как бы безошибочно,
казалось, программа бы не работала, всегда есть возможность для возникновения
непредвиденных ситуаций, когда VB не может выполнить корректно какой-нибудь
оператор, что приводит к аварийному завершению программы (фатальный крах). При
этом возможна еще и потеря данных, если в это время происходило, например, копирование
файла или передача информации. В большинстве своем это ошибки вызваны внешними
событиями, например пустой дисковод, когда твоя программа обращается к дискете,
отсутствие того файла, который ты пытаешься открыть для, например, чтения, а
также ошибки переполнения (когда не хватает памяти для рисунков и ли печати),
сетевые сбои, проблемы с буфером обмена и много чего еще. Скажу сразу, что предусмотреть
все возможные ситуации практически невозможно, особенно если твоим продуктом
будет пользоваться сторонний юзер. Юзера крайне изобретательны. Я не раз сталкивался
с возниковением таких ситуаций, спровоцированных юзером, которые у меня, например,
в голове не укладываются, и которые потом смоделировать практически невозможно.
Видимо по какому-то озарению свыше, юзер может добиться, таких катастрофических
результатов, которых я достигнуть при всей внутренней мобилизации сил своего
хилого организма не в состоянии. Если поставить перед собой цель создать программу,
всегда работающую корректно, можно раздуть код этой программы до безумных размеров
и все равно полного успеха не добиться. Microsoft, например, не добился.
Хотя и не все ошибки VB может перехватывать (их полный список
можно найти здесь), однако избегать
стандартных ошибок, обрабатывать явно возможные ошибки мы просто обязаны. Мне
представляется, что для исключения фатальных сбоев надо придерживаться следующей
стратегии.
Во-первых, код программы должен идеально работать, когда
все внешние источники данных (файлы, диски, принтер и т.п.) на месте, включены
и работоспособны, когда обрабатываются разумные для твоего компьютера объемы
информации. Для этого ты должен ясно представлять логику работы всего программного
кода, а не компоновать операторы в надежде, что они вместе будут выполняться
правильно точно также, как каждый в отдельности..
Во-вторых, надо стараться избегать возникновения ошибки
программно, тогда ее не придется отслеживать и обрабатывать. Ярким примером
такой ситуации может служить программа Scandisk, которую мы рассматривали
в прошлой, 10-ой главе. Когда мы тыкали мышью в Dir1 по директориям,
все было нормально, при этом выполнялся следующий оператор:
FileName = Dir(OurDir
& «*.*», 0) ‘присваиваем переменной значение
функции Dir для всех файлов
Если же мы кликали по верхней строке с именем не директории, а диска, возникала
ошибка 52 «Неправильное имя файла, или номер». А дело то всего лишь
в слеже, который мы поставили в операторе. Дело в том, что функция Dir
возвращает имя диска со слежом в конце строки, а имя директории — нет. Если
мы его уберем и оператор будет выглядеть так
FileName = Dir(OurDir
& «*.*», 0) ‘присваиваем переменной значение
функции Dir для всех файлов
вот тогда на диске оператор сработает правильно, зато перестанет читать директории.
Можно конечно обработать эту ошибку, о чем мы будем говорить ниже, по ее номеру,
но тогда в трех нижних операторах
Attr = GetAttr(OurDir
& «/» & FileName)…
той же причиной будет также вызвана ошибка, но с другим номером — 75 «Ошибка
доступа пути или файла». Тогда придется обрабатывать и ее. Это тоже вариант.
Но по-моему логичнее сразу записать в переменную Ourdir правильный путь
при любом раскладе. И надо-то для этого всего лишь убрать слеж из четырех операторов,
где возникает ошибка, а в процедуру Dir1_Change добавить проверку, которая
добавляет слеж при его отсутствии:
If
Right(OurDir, 1) <> «» Then
OurDir = OurDir & «» ‘добавленный оператор
для устранения ошибки
вот и все дела. Полный исправленный код, если что не понятно, можно скачать
здесь.
В-третьих, при обращении к внешним источникам, в том числе
к файлам, для получения или вывода информации надо предполагать, что возможно
они будут недоступны. Кроме того, надо подумать, какие еще фатальные ситуации
могут возникнуть. Вот тут уже надо обрабатывать ошибки конкретно.
Давайте создадим новый exe-проект. По мере необходимости на форму будем класть
командные кнопки и в их процедуре отлавливать ошибки. Возьмем пример самой распространенной
ошибки — нет того файла, который нам как раз крайне необходим. Положим на форму
кнопку Command1 (она будет называться Resume Next) и в ее процедуре напишем
операторы открытия файла «aaa.txt» для последовательного чтения.
Option Explicit
Private Sub Command1_Click()
Open "aaa.txt" For
Input As #1
Close #1
End Sub
Поскольку такого файла никто не создавал, то при запуске программы
и нажатии кнопки Command1 возникнет ошибка 53, о чем нам любезно сообщит
Windows: «Run-time error ’53’: Файл не найден». Программа аварийно
завершится.
Итак, что же нам может предложить VB для предотвращения фатальных ошибок.
Операторы
ON ERROR и RESUME (Resume Next).
Перво-наперво, ошибку надо отловить. Сделать это можно с помощью
оператора On Error. Однако сам по себе этот оператор работать не будет,
это и логично, с отловленной ошибкой ведь надо что-то делать. Самое простой
и безбашенный метод (кстати довольно эффективный, когда постоянно возникает
ошибка, справиться с ней не можешь и это тебя достало) — присобачить к оператору
On Error оператор Resume Next. Тогда получившийся оператор On
Error Resume Next в случае возникновения ошибки возвратится в программу
и передаст управление оператору, следующему за тем оператором, где возникла
ошибка. Однако следует помнить, что On Error Resume Next действует во
всей процедуре, и если процедура большая и сложная, то ошибка может возникнуть
совсем в другом операторе, нежели ты ожидаешь. Она все равно будет обработана,
VB перескочит на следующий оператор, а ты об этом ничего не узнаешь и будешь
дня два ломать голову, почему не работает то-то и то-то, хотя все прописано
в коде черным по-белому. Ну, тем не менее исправим наш код с использованием
оператора On Error:
Private Sub
Command1_Click()
On Error Resume Next
Open "aaa.txt" For
Input As #1
Close #1
Form1.Print "Процедура кое-как завершена"
Form1.Print "с помощью On Error Resume Next"
Form1.Print "файл не считан, работать не с
чем"
End Sub
Текст в операторах Form1.Print написан
условно, так как ясно, что он будет выводится независимо от того, есть ошибка
или нет. Т.е на самом деле пользователь никакой реальной информации об ошибке
не получит и поправить ничего не сможет. Иное дело, если мы к нашему On Error
попробуем пристроить оператор безусловного перехода GoTo метка.
На самом деле он унаследован от старого Бейсика и использовать его нужно крайне
осторожно. Этот оператор отсылает ( в нашем случае, если возникла ошибка) к
строке, помеченной меткой, но только внутри текущей процедуры. Положим следующую
кнопку Command2 (ее назание GoTo метка) и в ней напишем процедуру
Private Sub
Command2_Click()
Dim Msg As String
On Error GoTo ErrorMark
Open "aaa.txt" For
Input As #1
Close #1
Exit Sub
ErrorMark:
Msg = "Ой, наверно файл куда-то запропал, попробуйте восстановить!?"
MsgBox Msg, , "Караул, нет данных!"
End Sub
Таким образом, при возникновении ошибки оператор GoTo возвращает нас
в программу к строке, помеченной меткой ErrorMark. Под ней пишутся операторы,
которые выполняются для реакции программы (или юзера) на ошибку. Обратите внимание,
чтобы они не выполнялись в случае, когда ошибки нет, перед меткой поставлен
оператор Exit Sub, который прерывает выполнение процедуры, не дожидаясь
оператора End Sub. В качестве реакции на ошибку у нас выводится MessageBox
с советом для юзера. Обратите снова Ваше драгоценное внимание на то, что в сообщении
я употребил слово «наверное». Реально при открытии файла могут возникать
разные ошибки и я лишь только предполагаю, что у нас ошибка 53.
Как я говорил выше, в сложных и больших процедурах, чтобы не отлавливать ошибки
в других операторах оператор GoTo метка. надо выключить. Для этого,
после сомнительного оператора, могущего вызвать ошибку, надо вставить строку
On
Error GoTo 0
Тогда обработчик ошибок прекратит свою работу и ошибки в последующих операторах
отслеживаться не будут.
Если в подпрограмме обработки ошибок имеется возможность устранить причину ее
возникновения, то надо вернуться на тот же оператор и снова его выполнить, уже
без ошибки. Для этого подпрограмму обработки ошибок заканчивают оператором Resume.
Если же надо перейти к другой строке, помеченной меткой, то программу обработки
ошибок надо закончить оператором Resume метка. Ну а теперь перейдем
к самому интересному — анализу ошибок.
Объект
Err.
Вот как раз с помощью этого объекта мы можем проанализировать, какая ошибка
произошла, т.е. по сути, получить ее номер, и в зависимости от ситуации программно
решить вопрос об ее устранении или наоборот, заставить юзера производить какие-либо
судоржные действия.
Основным свойством объекта Err является свойство .Number, которое
и возвращает нам номер ошибки. Настолько основным, что .Number можно
не писать. Синтаксис будет такой:
Переменная=Err.Number или Переменная=Err
Это свойство для нас главное. Кроме того (для сведения) имеются еще такие свойства:
.Sourse — имя текущего проекта VB
. Description — cтрока, соответствующая строке, возвращаемой функцией
Error для кода ошибки, указанного в свойстве Number, если такая строка существует
.HelpFile — полное имя (включая диск и путь) файла справки Visual Basic.
. HelpContext — контекстный идентификатор файла справки Visual Basic,
соответствующий ошибке с кодом, указанным в свойстве Number.
. LastDLLError — содержит системный код ошибки для последнего вызова
DLL (только для 32-разрядных Microsoft Windows)
Положим на форму Text1 со свойством Multiline — True,
кнопку Command3 (название — Прочитать файл), а также для того, чтоб нам
было удобно создавать, убивать файл, положим еще три кнопки Command4,
Command5 и Command6, называющиеся соответственно «Создать
aaa.txt», «Создать пустой aaa.txt» и «Убить aaa.txt».
В процедурах этих трех кнопок напишем:
код для создания файла путем открытия и записи туда слов «не пустой»
в Command4
Private Sub
Command4_Click()
'создать aaa.txt
On Error Resume Next
Kill (App.Path & "aaa.txt")
Open App.Path & "aaa.txt" For
Output As #1
Print #1, "не пустой"
Close #1
Text1.Text = "Создан не пустой файл aaa.txt"
End Sub
в Command5 создадим пустой файл, т.е. файл не содержащий ни одного символа
Private Sub
Command5_Click()
'создать пустой aaa.txt
On Error Resume Next
Kill (App.Path & "aaa.txt")
Open App.Path & "aaa.txt" For
Output As #1
Close #1
Text1.Text = "Создан пустой файл aaa.txt"
End Sub
и в Command6 — программу для уничтожения файла
Private Sub
Command6_Click()
'убить aaa.txt
On Error Resume Next
Kill (App.Path & "aaa.txt")
Text1.Text = "Убит файл aaa.txt"
End Sub
Обратите внимание, что перед созданием файла, я убиваю старый. Так как старый
файл может на тот момент не существовать (в этом случае возникнет ошибка), я
использую On Error Resume Next.
Теперь создадим коротенькую процедуру загрузки формы для проверки в самом начале,
существует ли файл aaa.txt
Private Sub
Form_Load()
Text1.Text = ""
If Dir(App.Path & "aaa.txt") = ""
Then Text1.Text = "Файла aaa.txt не существует"
End Sub
А вот теперь в кнопке Command3 напишем полноразмерный обработчик ошибок.
Private Sub
Command3_Click()
'объявляем переменную для считывания данных из файла
Dim Stroka As String
'использование объекта Err при обработке ошибок
On Error GoTo ErrorMark
ForOpen:
Open App.Path & "aaa.txt" For
Input As #1
Line Input #1, Stroka
Text1.Text = Text1.Text & "Файл успешно открыт, содержание файла: "
& Stroka & vbCrLf
Close #1
Exit Sub
ErrorMark:
Close #1
'выводим возможные данные об ошибке
Text1.Text = "Возникла ошибка # " & Err.Number & vbCrLf
Text1.Text = Text1.Text & Err.Description & vbCrLf
Text1.Text = Text1.Text & "В " & Err.Source & vbCrLf
Text1.Text = Text1.Text & "Полный путь хелпа " & Err.HelpFile
& vbCrLf
Text1.Text = Text1.Text & "Идентификатор файла спраки VB " &
Err.HelpContext & vbCrLf
Text1.Text = Text1.Text & "DLL " & Err.LastDllError &
vbCrLf
'Ecли файла нет, то создем его
If Err = 53 Then
Open App.Path & "aaa.txt" For
Output As #1
Print #1, " "
Close #1
Text1.Text = Text1.Text & "Создан новый файл aaa.txt" & vbCrLf
'возвращаемся на исходную позицию
Resume ForOpen
'если файл пустой дописываем в него пробел
ElseIf Err = 62 Then
Open App.Path & "aaa.txt" For
Append As #1
Print #1, " "
Close #1
Text1.Text = Text1.Text & "Ошибка файла aaa.txt исправлена " &
vbCrLf
'возвращаемся на исходную позицию
Resume ForOpen
Else
Text1.Text = Text1.Text & "Не знаю, что делать с ошибкой, файл не открыт
"
End If
End Sub
Теперь, если мы убъем файл кнопкой «Убить aaa.txt» и
нажмем кнопку «Прочитать файл», то в Text1 выведется вся доступная
информация об ошибке, создастся новый файл и процедура начнет выполнятся по-новой
уже с существующим файлом. Аналогичная ситуация возникает при создании пустого
файла. При чтении такого файла возникает ошибка 62 — чтение после конца файла.
Мы также получаем информацию об ошибке, дописываем в файл пробел и по Resume
вновь запускаем процедуру. Если возникает ошибка с номером, который мы не знаем,
то просто выводим об этом сообщения в Text1.
Замечу, что с операторами Open и Close надо обращаться
аккуратно. Все файлы должны своевременно открываться и закрываться. Если путаешься
с работой с файлами, посмотри Главу 7. Скачать
исходник можешь вверху страницы.
Резюме:
Таким образом, обработка ошибки включает в себя:
— Обнаружение ошибки On Error.
— Переход на подпрограмму обработки GoTo (или, на крайняк, на следующий
оператор с помощью Resume Next).
— Определение ошибки Err.Number и в зависимости от этого выполнение каких-то
действий.
— Возврат Resume после устранения ошибки на оператор, вызвавший ошибку.
Для очистки совести скажу еще про функцию
Error. Она по номеру ошибки выводит пояснение — что это за ошибка. Синтаксис:
Строковая переменная=Error(номер ошибки)
Если хотите, положите на форму последнюю кнопку Command7 и для вывода
пояснений к ошибкам, например с 52 по 62 напишите такой код и посмотрите, что
получится.
Private Sub
Command7_Click()
Dim ErrorNumber As Long
Text1.Text = ""
For ErrorNumber = 52 To
64
Text1.Text = Text1.Text & Error(ErrorNumber) & vbCrLf
Next ErrorNumber
End Sub
Совершенно ничего сложного. Ну-с с этим все.
Сайт создан в системе uCoz
First of all, go get MZTools for Visual Basic 6, its free and invaluable. Second add a custom error handler on every function (yes, every function). The error handler we use looks something like this:
On Error GoTo {PROCEDURE_NAME}_Error
{PROCEDURE_BODY}
On Error GoTo 0
Exit {PROCEDURE_TYPE}
{PROCEDURE_NAME}_Error:
LogError "Error " & Err.Number & " (" & Err.Description & ") in line " & Erl & _
", in procedure {PROCEDURE_NAME} of {MODULE_TYPE} {MODULE_NAME}"
Then create a LogError function that logs the error to disc. Next, before you release code add Line Numbers to every function (this is also built into MZTools). From now on you will know from the Error Logs everything that happens. If possible, also, upload the error logs and actually examine them live from the field.
This is about the best you can do for unexpected global error handling in VB6 (one of its many defects), and really this should only be used to find unexpected errors. If you know that if there is the possibility of an error occurring in a certain situation, you should catch that particular error and handle for it. If you know that an error occurring in a certain section is going to cause instability (File IO, Memory Issues, etc) warn the user and know that you are in an «unknown state» and that «bad things» are probably going happen. Obviously use friendly terms to keep the user informed, but not frightened.
Ошибками пользуются в любой нормально написанной программе,
вне зависимости от объема ее кода.
Это помогает проанализировать, правильно ли работает программа.
И если нет, минимальными усилиями узнать причину проблемы.
Для этого в идеале, в каждой из функций следует использовать обработчик ошибок (On Error Goto).
А там, где используются API-функции, каждую из них проверять на предмет возвращаемого значения, а также кода ошибки API-функции (Err.LastDllError).
Это позволит минимизировать затраты на отладку программы
и еще на этапе проектирования исключить некоторые наиболее вероятные ошибки в вызове функций.
Внутреннюю ошибку VB можно также симмитировать вручную с помощью вызова процедуры Err.Raise [Номер ошибки]
Это спровоцирует переход к метке обработчика ошибок, указанной в директиве On Error Goto Имя_Метки
Это иногда полезно, если Вы хотите ввести в программу собственные ошибки (выход из функции при возникновении, на Ваш взгляд, критической ситуации).
Номер внутренней ошибки также можно очистить методом Err.Clear
Обработчик ошибок в любой момент можно отключить, вернув стандартное поведение программы, с помощью директивы On Error Goto 0
В этом случае, если произойдет ошибка, программа прекратит свое выполнение и выведет ошибку и краткое описание в стандартном диалоговом окне (msgbox)*
* Кроме случаев, когда обработчик ошибок установлен в родительской функции (вниз по стеку вызовов), т.е. функции, которая вызвала эту функцию. В этом случае будет вызван именно её обработчик, а программа продолжит свое выполнение.
При возникновении внутренней ошибки, ее номер и описание можно получить через свойства Number и Description объекта Err.
Стандартный обработчик ошибок VB имеет такой вид:
Function foo()
On Error Goto ErrorHandler
' ... тело функции
if {что-то можем проверить} then
' здесь, если нам нужно, можем вызвать ошибку самостоятельно
err.Raise 51 'Internal error
end if
Exit function
ErrorHandler:
'обработчик
'выводим номер внутренней ошибки, краткое описание, номер ошибки API-функции
Debug.? "Error: " & Err.Number & ". " & Err.Description & ". LastDllErr: " & Err.LastDllError
End function
Описание ошибки API-функции (или COM-объекта) можем получить с помощью такой функции:
Private Declare Function FormatMessage Lib "kernel32.dll" Alias "FormatMessageA" (ByVal dwFlags As Long, lpSource As Long, ByVal dwMessageId As Long, ByVal dwLanguageId As Long, ByVal lpBuffer As String, ByVal nSize As Long, Arguments As Any) As Long
Const MAX_PATH As Long = 260&
Public Function MessageText(lCode As Long) As String
On Error goto ErrorHandler
Const FORMAT_MESSAGE_FROM_SYSTEM As Long = &H1000&
Const FORMAT_MESSAGE_IGNORE_INSERTS As Long = &H200
Dim sRtrnCode As String
Dim lRet As Long
sRtrnCode = Space$(MAX_PATH)
lRet = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS, ByVal 0&, lCode, ByVal 0&, sRtrnCode, MAX_PATH, ByVal 0&)
If lRet > 0 Then
MessageText = Left$(sRtrnCode, lRet)
MessageText = Replace$(MessageText, vbCrLf, vbNullString)
End If
Exit function
ErrorHandler:
Debug.? "Error: " & Err.Number & ". " & Err.Description & ". LastDllErr: " & Err.LastDllError
End Function
Список внутренних ошибок VB6:
Код|Описание
3|Return without GoSub
5|Invalid procedure call or argument
6|Overflow
7|Out of memory
9|Subscript out of range
10|This array is fixed or temporarily locked
11|Division by zero
13|Type mismatch
14|Out of string space
16|Expression too complex
17|Can’t perform requested operation
18|User interrupt occurred
20|Resume without error
28|Out of stack space
35|Sub or Function not defined
47|Too many DLL application clients
48|Error in loading DLL
49|Bad DLL calling convention
51|Internal error
52|Bad file name or number
53|File not found
54|Bad file mode
55|File already open
57|Device I/O error
58|File already exists
59|Bad record length
61|Disk full
62|Input past end of file
63|Bad record number
67|Too many files
68|Device unavailable
70|Permission denied
71|Disk not ready
74|Can’t rename with different drive
75|Path/File access error
76|Path not found
91|Object variable or With block variable not set
92|For loop not initialized
93|Invalid pattern string
94|Invalid use of Null
96|Unable to sink events of object because the object is already firing events to the maximum number of event receivers that it supports
97|Can not call friend function on object which is not an instance of defining class
98|A property or method call cannot include a reference to a private object, either as an argument or as a return value
321|Invalid file format
322|Can’t create necessary temporary file
325|Invalid format in resource file
380|Invalid property value
381|Invalid property array index
382|Set not supported at runtime
383|Set not supported (read-only property)
385|Need property array index
387|Set not permitted
393|Get not supported at runtime
394|Get not supported (write-only property)
422|Property not found
423|Property or method not found
424|Object required
429|ActiveX component can’t create object
430|Class does not support Automation or does not support expected interface
432|File name or class name not found during Automation operation
438|Object doesn’t support this property or method
440|Automation error
442|Connection to type library or object library for remote process has been lost. Press OK for dialog to remove reference.
443|Automation object does not have a default value
445|Object doesn’t support this action
446|Object doesn’t support named arguments
447|Object doesn’t support current locale setting
448|Named argument not found
449|Argument not optional
450|Wrong number of arguments or invalid property assignment
451|Property let procedure not defined and property get procedure did not return an object
452|Invalid ordinal
453|Specified DLL function not found
454|Code resource not found
455|Code resource lock error
457|This key is already associated with an element of this collection
458|Variable uses an Automation type not supported in Visual Basic
459|Object or class does not support the set of events
460|Invalid clipboard format
461|Method or data member not found
462|The remote server machine does not exist or is unavailable
463|Class not registered on local machine
481|Invalid picture
482|Printer error
735|Can’t save file to TEMP
744|Search text not found
746|Replacements too long
В этом разделе Вы познакомитесь с концепцией обработки ошибок. Кроме того, Вам станет известна важная часть языка, называемая условными операторами.
В конце прошлого подраздела мы столкнулись с ситуацией, когда при некорректном действии пользователя программа вывела окно-предупреждение и остановилась. Согласитесь, если оставить программе возможность так себя вести, ею никто не станет пользоваться. Один из способов справиться с ситуацией – т.н. обработчик ошибок.
-
VB прослеживает появление ошибок при выполнении программы. С обычным поведением языка Вы только что столкнулись – программа останавливается и появляется окно-предупреждение. При проектировании программы всегда следует предвидеть возможные ошибки. Справиться с этим можно несколькими способами.
Существует конструкция языка On Error (При Ошибке). Она бывает следующих видов:
-
On Error Resume Next (При Ошибке Продолжить Дальше) – при появлении ошибки она игнорируется и выполнение продолжается. Вы должны очень хорошо предвидеть виды ситуаций, могущих вызвать ошибку, если упустите какую-то ситуацию, возможен крах системы. Но в большинстве случаев этот способ подходит.
-
On Error Goto <метка> (При Ошибке ПерейтиНа <метку>) – при ошибке программа переносит выполнение на <метку>, после которой следует конструкция, обрабатывающая ошибку.
-
On Error Goto 0 (При Ошибке ПерейтиНа Ноль) – отмена обработки ошибок.
-
Все конструкции On Error действуют в пределах процедуры, в которой находятся (о процедурах Вы узнаете далее).
Другой способ выявления ошибок – проверка т.н. системного объекта Err (от англ. Err[or] – Ошибка). Этот объект имеет свойства Number (Номер) и Description (Описание), учитывая которые вы можете так или иначе влиять на ситуацию. Этот метод сложнее, но гибче. Впоследствии мы познакомимся с конструкциями VB, которые позволят Вам пользоваться объектом Err.
В нашем случае применим способ игнорирования ошибок – очевидно, что ошибки будут возникать только из-за недопустимых действий с БД, и эти ошибки имеют нефатальный характер.
В начало обработчика события cmdUpdate_Click внесите строку:
On Error Resume Next
Код примет вид:
Private Sub cmdUpdate_Click()
On Error Resume NextdtPhoneBook.Recordset.Update
End Sub
-
Запустите программу.
-
Добавьте запись кнопкой «Добавить».
-
Внесите значения в текстовые поля.
-
Щелкните кнопку «Обновить».
Как видите, без перехода на другую строку содержимое сетки обновилось (и содержимое БД тоже).
Остался обработчик события cmdDelete_Click. Здесь ситуация немного сложнее.
В обработчик cmdDelete_Click добавьте строку:
dtPhoneBook.Recordset.Delete
Но при удалении записи указатель объекта Recordset не сможет переместиться самостоятельно и будет указывать на уже удаленную, несуществующую запись, это неверно.
-
Следует явно переместить указатель:
dtPhoneBook.Recordset.MoveNext
Сразу следует предусмотреть ситуацию удаления последней записи в наборе – т.е., если перемещать указатель некуда.
VB позволяет производить проверку условия с помощью слов If(Если), Then (То), Else (Иначе).
-
Типичный вид проверки условия:
If <условие> Then
<действия, если условие выполнено>
Else
<действие, если условие не выполнено>
End If
Возможны разновидности этой конструкции:
-
Так можно делать, если <действие> короткое и поместится в строку;
If <условие> Then <действие>
-
А так делается, если проверяется сразу несколько условий.
If <условие> Then
<действия, если условие выполнено>
ElseIf <другое условие> Then
<действие, если другое условие выполнено>
.
.
.
Else
<действие, если ни одно из условий не выполнено>
End If
-
В случае, если проверяется, приняло ли какое-то свойство значение из списка возможных, применяется конструкция:
Select Case <свойство>
Case <значение1>
<действия при значении1>
Case <значение2>
<действия при значении2>
Case <значение3>
<действия при значении3>
.
.
.
CaseElse
<действия при других значениях>
End Select
В нашем случае будем производить проверку, не достигли ли мы конца набора записей, и, если достигли, то будем перемещать указатель набора.
В обработчик события cmdDelete_Click внесите строки:
If dtPhoneBook.Recordset.EOF Then
End If
Это – заготовка конструкции проверки условия.
-
Буквально это значит:
Если выполняется условие dtPhoneBook.Recordset.EOF, То
-
Конструкция обязательно завершается признаком конца проверки условия – строкой
End If
Свойство EOF (КонецНабора) может принимать два значения: True (Истина) и False (Ложь). Если достигается конец набора (условие выполняется), то свойство принимает значение True, в других случаях его значение равно False.
-
Значения True(Правда) и False (Ложь) – т.н. логические или булевские величины. Они применяются в логических операциях при проверке условий. VB имеет несколько слов-операторов для обработки логических условий.
-
Вышеуказанные строки можно написать так:
If dtPhoneBook.Recordset.EOF = True Then
.
.
.
End If
Это – равнозначные способы написания. Первый способ проще, но второй – нагляднее, выбирайте сами.
Теперь определите действие, которое будет происходить в ответ на выход указателя за пределы набора (а свойство КонецНабора – указывает не на последнюю запись, а на участок после последней записи).
-
Логично будет перенести указатель на последнюю запись:
dtPhoneBook.Recordset.MoveLast
-
Код примет вид:
Private Sub cmdDelete_Click()
dtPhoneBook.Recordset.DeleteIf dtPhoneBook.Recordset.EOF ThendtPhoneBook.Recordset.MoveLastEnd If
End Sub
Внесите в код соответствующие строки.
Все? Нет, мы упустили случай удаления последней и единственной записи в наборе, т.е., когда набор остается пуст. Можно проверять количество записей в наборе, и, если осталась последняя запись, запрещать удаление, но мы сделаем проще – проигнорируем ошибку.
-
Добавьте второй строкой обработчика
On Error Resume Next
-
Запустите программу.
Все работает!
Хитрость:
Напишите так:
Private Sub cmdDelete_Click()
On Error Resume NextWith dtPhoneBook.Recordset.DeleteIf .EOF Then.MoveLastEnd IfEnd With
End Sub
Этот код действует совершенно так же, как и вышеописанный.
Слово With (С, Совместно) заставляет VB считать, что все действия производятся с объектом, чье имя стоит за With (здесь — dtPhoneBook.Recordset), и Вы можете сразу «дописывать» фразу, начиная ее с точки.
-
Конструкция With должна быть внутри процедуры (т.е., на другие процедуры она не распространяется), и обязательно должна завершаться строкой End With.
-
Кроме того, такое написание кода, как правило, работает быстрее.
Применяйте конструкцию With только тогда, когда обращение к одному и тому же объекту встречается не менее 3-5 раз подряд.
Проверьте, как работает Ваша программа.
Не забудьте сохранить результаты.
Вопросы
-
Какие способы обработки ошибок Вам известны?
-
Какие условные операторы Вам известны? В каких случаях они применяются?
Задания
-
Обязательно добавьте комментарии к созданной Вами форме!
-
Законспектируйте раздел.
-
Так же. как и форму frmPhoneBook, создайте вторую форму для работы с базой данных – форму frmDiary. Внешний вид этой формы и составляющие ее компоненты совершенно аналогичны уже созданной «Телефонной книжке», другими будут имена элментов управления и надписи на метках. Естественно, форма будет подключена к другой таблице БД:
Элемент управления | Свойства |
dtDiary |
DataBaseName = C:VB BeginOrganizer.mdb RecordSource = Diary |
dbgrDiary |
DataSource = dtDiary |
txtNote |
DataSource = dtDiary DataField = Note |
txtDate |
DataSource = dtDiary DataField = Dates |
Соответственно следует изменить код формы, поменяв имена объектов там, где они отличаются от имен формы frmPhoneBook.
В остальном эти формы одинаковы.
Сохраните и эту форму в рабочем каталоге проекта.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
- visitor
- Новичок
- Сообщения: 25
- Зарегистрирован: 08.10.2008 (Ср) 1:52
Обработка ошибок в VB6
Я не большой спец в механизме обработки исключений, всю жизнь мечтал в ВБ6 иметь возможность в одном месте написать что-то типа
- Код: Выделить всё
try{App.Run()}
catch()...
Можно ли достичь такого эффека в ВБ ?
Пробовал в класее звпустить свой цикл обработки сообщений
- Код: Выделить всё
Public Function Run() As Long
Dim xmsg As MSG
Dim bRet As Long
On Error GoTo ErrH
bRet = GetMessage(xmsg, 0, 0, 0)
Do While ((bRet))
If bRet = -1 Then
MsgBox "fatal error -1", vbCritical
Else
TranslateMessage xmsg
DispatchMessage xmsg
End If
bRet = GetMessage(xmsg, 0, 0, 0)
Loop
GoTo FnEx
ErrH:
MsgBox "Error encounted " + vbCrLf + CStr(Err.Number) + " " + Err.Description
Resume Next
FnEx:
End Function
Не получаю нужного эффекта..Подскажите. посоветуйте. пожалуйста.
- Хакер
- Телепат
-
- Сообщения: 16462
- Зарегистрирован: 13.11.2005 (Вс) 2:43
- Откуда: Казахстан, Петропавловск
-
- Сайт
- ICQ
Re: Обработка ошибок в VB6
Хакер » 01.03.2009 (Вс) 14:50
Молодец, но причём тут MessageLoop?
—We separate their smiling faces from the rest of their body, Captain.
—That’s right! We decapitate them.
- visitor
- Новичок
- Сообщения: 25
- Зарегистрирован: 08.10.2008 (Ср) 1:52
Re: Обработка ошибок в VB6
visitor » 01.03.2009 (Вс) 15:11
Хакер писал(а):Молодец, но причём тут MessageLoop?
Это хоть как-то похоже на ответ на мой вопрос, или дополнительный вопрос с целью ответить на вопрос, или на намек на ответ?
- Хакер
- Телепат
-
- Сообщения: 16462
- Зарегистрирован: 13.11.2005 (Вс) 2:43
- Откуда: Казахстан, Петропавловск
-
- Сайт
- ICQ
Re: Обработка ошибок в VB6
Хакер » 01.03.2009 (Вс) 15:27
Это хоть как-то похоже на ответ на мой вопрос, или дополнительный вопрос с целью ответить на вопрос, или на намек на ответ?
Это вопрос, причиной задания которого было желание получить ответ на тему того, по какой причине в первом сообщении упоминается Message pumping loop, потому как первое сообщение, содержащее это упоминание, навело на мысль о возможном непонимании каких-то моментов, о вероятно сложившимся у автора темы ошибочном убеждении о наличии какой-то связи между обработкой исключений и обработкой очереди оконных сообщений, потому как подобная связь очевидно отсутствует, и упоминание MPL в вопросе об exception handling кажется странным.
—We separate their smiling faces from the rest of their body, Captain.
—That’s right! We decapitate them.
- visitor
- Новичок
- Сообщения: 25
- Зарегистрирован: 08.10.2008 (Ср) 1:52
Re: Обработка ошибок в VB6
visitor » 01.03.2009 (Вс) 15:37
по моему мнению on error обрабатывает ошибки в текущей процедуре и во всех вложенных. Для этого я попытался организовать место (цикл обработки сообщений) , являющееся вершиной иерархии вызовов (именно в моем , юзерском коде).
- Zenitchik
- Постоялец
- Сообщения: 369
- Зарегистрирован: 21.12.2006 (Чт) 14:48
Re: Обработка ошибок в VB6
Zenitchik » 01.03.2009 (Вс) 20:43
ЕМНИП, это не так. Но я могу ошибаться.
Если администрация не возражает, я задам в этой теме еще два вопроса:
1. Как заставить ошибку «всплывать» в вызывающую процедуру?
2. Как сделать это с генерируемой Err.Raise ошибкой?
Знание английского языка — затрудняет понимание кода
- visitor
- Новичок
- Сообщения: 25
- Зарегистрирован: 08.10.2008 (Ср) 1:52
Re: Обработка ошибок в VB6
visitor » 01.03.2009 (Вс) 21:06
Позволю себе дописать еще пару подвопросов:
Мне интересна разница On Error и On Local Error
Влияет ли на механизм обработки ошибок в скомпилированной программе выбор режима в Tools->Options->General->Error trapping
Если можно кто что знает, распишите максимально понятно
Заранее спасибо !
- tyomitch
- Пользователь #1352
-
- Сообщения: 12822
- Зарегистрирован: 20.10.2002 (Вс) 17:02
- Откуда: חיפה
Re: Обработка ошибок в VB6
tyomitch » 01.03.2009 (Вс) 21:31
Zenitchik писал(а):1. Как заставить ошибку «всплывать» в вызывающую процедуру?
2. Как сделать это с генерируемой Err.Raise ошибкой?
On Error GoTo 0
visitor писал(а):Влияет ли на механизм обработки ошибок в скомпилированной программе выбор режима в Tools->Options->General->Error trapping
Нет
- tyomitch
- Пользователь #1352
-
- Сообщения: 12822
- Зарегистрирован: 20.10.2002 (Вс) 17:02
- Откуда: חיפה
Re: Обработка ошибок в VB6
tyomitch » 01.03.2009 (Вс) 21:33
visitor писал(а):по моему мнению on error обрабатывает ошибки в текущей процедуре и во всех вложенных.
Верно.
- ANDLL
- Великий гастроном
-
- Сообщения: 3450
- Зарегистрирован: 29.06.2003 (Вс) 18:55
-
- ICQ
Re: Обработка ошибок в VB6
ANDLL » 01.03.2009 (Вс) 21:39
Оконная функция, которую вызывает DispatchMessage содержит в себе обработчик исключений, который завершает программу.
Именно поэтому перехватат ошибок, который ожидает автор, не происходит
Гастрономия — наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог
- Zenitchik
- Постоялец
- Сообщения: 369
- Зарегистрирован: 21.12.2006 (Чт) 14:48
Re: Обработка ошибок в VB6
Zenitchik » 01.03.2009 (Вс) 23:19
Позвольте, разве On Error GoTo 0 не отменяет юзерскую обработку ошибок вообще?
Тогда при возникновении ошибки произойдет остановка выполнения. А мне нужно, чтобы прервано было только выполнение текущей процедуры, и сработал обработчик уровнем выше.
Знание английского языка — затрудняет понимание кода
- ANDLL
- Великий гастроном
-
- Сообщения: 3450
- Зарегистрирован: 29.06.2003 (Вс) 18:55
-
- ICQ
Re: Обработка ошибок в VB6
ANDLL » 01.03.2009 (Вс) 23:33
Нет, не отменяет обработку вообще. Отменяет обработку в текущей процедуре
Гастрономия — наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог
- Zenitchik
- Постоялец
- Сообщения: 369
- Зарегистрирован: 21.12.2006 (Чт) 14:48
Re: Обработка ошибок в VB6
Zenitchik » 05.03.2009 (Чт) 18:59
Т.е. обработчик в вызывающей процедуре ошибку перехватит?
Тогда другой вопрос: я хочу, чтобы в случае некорректных значений аргументов (проверка моя) происходила ошибка в вызывающей процедуре. Как этого добиться?
Знание английского языка — затрудняет понимание кода
- ANDLL
- Великий гастроном
-
- Сообщения: 3450
- Зарегистрирован: 29.06.2003 (Вс) 18:55
-
- ICQ
Re: Обработка ошибок в VB6
ANDLL » 05.03.2009 (Чт) 20:25
err.raise
Гастрономия — наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог
- Zenitchik
- Постоялец
- Сообщения: 369
- Зарегистрирован: 21.12.2006 (Чт) 14:48
Re: Обработка ошибок в VB6
Zenitchik » 05.03.2009 (Чт) 21:47
А логика работы?
Я вызываю Err.Raise. Если обработчика нет — это мне никакого толку не даст.
Если обработчик в вызывающей процедуре — он ее перхватывает, так?
А что нужно предпринять, чтобы узнать источник ошибки? Кстати, как вообще после отмены нумерации строк получают источник ошибки?
Знание английского языка — затрудняет понимание кода
- ANDLL
- Великий гастроном
-
- Сообщения: 3450
- Зарегистрирован: 29.06.2003 (Вс) 18:55
-
- ICQ
Re: Обработка ошибок в VB6
ANDLL » 05.03.2009 (Чт) 22:17
Вообще error handling в VB это огромная ж-па.
Более того, error handling — самый серьезный минус VB6 перед хорошими ЯП.
Номер строки можно указать. Если номер указан, он сохраняется в объекте ошибки, если не указан — никак не узнать.
Stack Trace никак не получить. Как либо фильтровать ошибки то же нельзя.
Гастрономия — наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог
- alibek
- Большой Человек
- Сообщения: 14205
- Зарегистрирован: 19.04.2002 (Пт) 11:40
- Откуда: Russia
Re: Обработка ошибок в VB6
alibek » 05.03.2009 (Чт) 22:35
ANDLL писал(а):Вообще error handling в VB это огромная ж-па.
Более того, error handling — самый серьезный минус VB6 перед хорошими ЯП.
Это да. Тяжелое наследие древнего BASIC.
Поэтому для более-менее нормальной обработки ошибок приходится сочинять свою сложную систему кодов ошибок плюс добавлять пяток глобальных переменных плюс класс-коллекцию для стека и отслеживания состояний. Эти действия, конечно, можно автоматизировать с помощью аддонов, но все-равно они остаются кривыми костылями. После try…catch остается только вздыхать и мириться с on error.
Лично я стараюсь обходится только on error resume next (с анализом ошибок сразу после мест их возможного возникновения), либо используя аргумент ByRef ErrorCode/ErrorMessage в вызываемой процедуре, который возвратит код/описание ошибки. Это не так правильно, но зато куда проще.
Lasciate ogni speranza, voi ch’entrate.
- Хакер
- Телепат
-
- Сообщения: 16462
- Зарегистрирован: 13.11.2005 (Вс) 2:43
- Откуда: Казахстан, Петропавловск
-
- Сайт
- ICQ
Re: Обработка ошибок в VB6
Хакер » 05.03.2009 (Чт) 22:43
А что общественность хочет?
—We separate their smiling faces from the rest of their body, Captain.
—That’s right! We decapitate them.
- Zenitchik
- Постоялец
- Сообщения: 369
- Зарегистрирован: 21.12.2006 (Чт) 14:48
Re: Обработка ошибок в VB6
Zenitchik » 06.03.2009 (Пт) 22:53
Хм… Следовательно, ничего умнее зашифровывания данных о месте генерации ошибки в ее номер и описание не придумать?
Хакер
Лично мне охота облегчить себе поиск ошибок. Если я знаю, что определенный тип ошибки происходит по вине вызывающей процедуры — я хочу, чтобы остановка выполнения произошла именно в ней.
Знание английского языка — затрудняет понимание кода
- Debugger
- Продвинутый гуру
-
- Сообщения: 1667
- Зарегистрирован: 17.06.2006 (Сб) 15:11
-
- ICQ
Re: Обработка ошибок в VB6
Debugger » 06.03.2009 (Пт) 23:14
Хакер писал(а):А что общественность хочет?
Может это можно исправить при помощи аддона?
(а потом добавим гибкую поддержку типов, то, се, другое, третье, и в итоге получим новый Си )
- Хакер
- Телепат
-
- Сообщения: 16462
- Зарегистрирован: 13.11.2005 (Вс) 2:43
- Откуда: Казахстан, Петропавловск
-
- Сайт
- ICQ
Re: Обработка ошибок в VB6
Хакер » 06.03.2009 (Пт) 23:31
Вопрос был в том, каким общественность хочет видеть механизм обработки ошибок. Это просто дискуссионный вопрос, а не экспресс-опрос мнений перед тем как броситься за написание эддина.
—We separate their smiling faces from the rest of their body, Captain.
—That’s right! We decapitate them.
- visitor
- Новичок
- Сообщения: 25
- Зарегистрирован: 08.10.2008 (Ср) 1:52
Re: Обработка ошибок в VB6
visitor » 09.03.2009 (Пн) 12:26
Хочется в основном получить возможность избавится от стандартного обработчика ошибок, заменив его своим…
Как минимум чтоб иметь возможность реализовать что-то вроде Send Report to developer… Стек вызовов и тп — уже мечта конечно
—-
Хотя я сегодня от квип инфиум получил сообщение о недостатке прав на создание файла, так там помимо стека вызовов куча всего красивого в репорте есть, даже дизассембли точки останова… писано на делфи… Канкуренты блин
—-
[добавлено]
к стати, что думаете по этому поводу ?? стоит попробовать ?
http://www.rsdn.ru/article/baseserv/except.xml
Вернуться в Visual Basic 1–6
Кто сейчас на конференции
Сейчас этот форум просматривают: Diamock, Google-бот, The trick и гости: 1