Имя: Пароль:
1C
 
Путь дятла
0 romix
 
28.03.05
03:37
Представим такую ситуацию: вы приобретаете ПО. Ситуация не то чтобы для России необычная, но, тем не менее, знаменательная. Предположим для большей ясности, что это программный комплекс для автоматизации торгового центра. После того, как специалисты вам все установили, обучили персонал и усталые, но довольные, уехали, вы очень рады благоприобретенному комплексу, хорошо работающим ключам его защиты, и, более того, хорошему отношению к вам со стороны действительно грамотных (я не шучу) менеджеров, специалистов и внедренцев.

Но со временем ваша радость может постепенно угаснуть.

- Программа 1С (а в действительности - подключенные к ней внешние компоненты) время от времени дает сбои. "Программа выполнила недопустимую операцию и будет закрыта".  Помимо очевидного неудобства для пользователя (программа 1С с треском закрывается), это, очевидно, не очень полезно для целостности базы данных.
- Информационный обмен, например, от кассы в программу или из программы в кассу "прогружает" не все данные. Это означает, что клиент (и его покупатели) будут щелкать лицом (или клювом) перед кассой, если нужный товар в кассу все-таки не пришел.
- Зависает или постоянно дает сбои компонента для электронных весов, когда в них посылают из компьютера информацию о товарах. Это особенно удобно, когда вам срочно нужно что-то взвешивать.
- Документы возврата товара, к огорчению бухгалтеров, сдающих отчетность (и к радости бдительных налоговых инспекторов), проводятся некорректно.
- Если вы выбираете вариант обмена через электронную почту, то обмен либо не работает совсем, либо дает какие-то неясные сбои с кодом ошибки 501 (хорошо, не 666 или не 911).
- Центральная информационная база не может корректно получить сведения из периферийных баз, и наоборот.

Я намеренно не называю конкретного поставщика ПО, поскольку это в данном случае не важно (и выясняется простым опросом других пользователей перед покупкой программного комплекса). Конечно, программ без ошибок не бывает, но если позволить себе привести аналогию, то никому не хочется ехать в поезде и все время ожидать крушения. Также никому не хочется лететь на большом самолете и ожидать, что, например, отвалится хвост. И уж совсем очевидно, что никому не хочется плыть на корабле и ожидать подводных камней.

Как и в случае с транспортом, в области разработки программного обеспечения, разумеется, тоже есть технические методики, которые помогают выявлять сбойный программный код, и не допускать его "крушения". Не претендуя на полноту и 100% достоверность, осмелюсь привести кое-какие соображения на этот счет.

------------------

Если вы программист, то, наверное, мечтаете о среде разработки, в которой ошибки "выявляют себя сами".  Подобное чудо, если его правильно применять, уже реализовано в ряде компиляторов, и даже положено в основу такой среды исполнения, как Microsoft .NET.

Разумеется, без тотального (полного) контроля ошибок в любой разработке, если поставлена цель сделать действительно качественное ПО, никому не обойтись. Но сильно ли от этого усложнится и вырастет программный код? Для примера возьмем фрагмент кода на языке Delphi, который устанавливает соединение с почтовым сервером.

//////////////////////////////////////////////////////////////

procedure T_SMTP.Connect;
begin
     tcp.HostName:=HostName;
     tcp.Port:=Port;
     tcp.Timeout:=Timeout;

     try  //***

       tcp.Connect;

       GetResultMessage;

     except  //***
       on E: Exception do Error('Ошибка подключения: '+e.Message); //***
     end; //***


     if ResultCode<>'220' then //***
       Error('Ошибка соединения с сервером: '+ResultMessage) //***
end;

Строки, помеченные тремя звездочками - это контроль ошибок. Если их убрать, то программа будет работать все еще хорошо (а иногда - даже очень устойчиво), но при возникновении любых проблем пользователь такого кода будет много часов выяснять проблему, "отвисать" на телефоне со службой тех. поддержки и изучать "Руководство для дятлов по долбежке бетонных столбов" (c) Пит.

Вы спросите, что делает загадочная процедура Error, и почему после нее нет явного выхода из функции по ошибке? А делает она следующее:

//////////////////////////////////////////////////////////////
procedure T_SMTP.Error(s:ShortString);
begin
 Raise EZeroDivide.Create(s);
end;

Оператор Raise (аналогичные операторы есть во многих языках программирования) возбуждает исключительную ситуацию. Он очень удобен, если вам нужно выйти сразу из нескольких вложенных функций или циклов, и при этом не усложнять программу контролем кодов возврата. (Почему именно EZeroDivide? Так в нашем примере будет проще).

Изловить возбужденное (или возбУжденное?) исключение позволяет хорошо известная всем программистам (и даже 1С-никам) конструкция try-except

try

 //   Некий большой и сложный код, который может сбойнуть

except  
       on E: Exception do begin
            ShowMessage('Ошибка подключения: '+e.Message);
       end;
end;

Позвольте пояснить работу этого обработчика исключения небольшим примером. Если дятел (птица) долбит дерево, и оно в этот момент падает (старое попалось, жуки-древоточцы изнутри источили..), то дятел прекращает процедуру долбления, и отлетает от дерева в безопасное место. Иначе бы падающее дерево "выполнило недопустимую операцию", и дятлу, продолжавшему его долбить, могло бы частично или полностью не поздоровиться (частичная или полная потеря данных). Аналогично поступают обработчики исключений - если в программе что-то идет не так (исключение), выполнение программы прекращается, и управление передается на оператор, следующий за ключевым словом except (то есть, это то самое "безопасное место", куда программа, при возникновении проблемы, должна "полететь").

Вы можете показать ошибку пользователю, как в представленном примере, в виде окна с кнопкой ОК (ShowMessage). Можно не показывать ее пользователю, а записать ее в журнал ошибок. Но в случае, например, внешних компонент 1С и то, и другое решение, скорее всего, будет не совсем правильным. Дело в том, что любые ошибки должны отрабатываться на самом высоком уровне (с) Керниган и Пайк. Поэтому в компонентах 1С, наверное, будет лучше возбудить собственное, 1С-ное исключение:

///////////////////////////////////////////////////////////////
function AddInObject.CallAsFunc(lMethodNum: Integer; var pvarRetValue: OleVariant; var paParams: PSafeArray): HResult; stdcall;
{Здесь 1С выполняет код внешних функций}
begin
    try
//...
//...
//...
    except
       on E: Exception do ShowErrorLog(E.Message);
    end;
    CallAsFunc := S_OK;
end;

Что же делает процедура ShowErrorLog()? Она показывает сообщение "красненьким" в окне сообщений 1С. Приведу ее код.

procedure AddInObject.ShowErrorLog(fMessage:WideString);
var
 ErrInfo: PExcepInfo;
begin
 If Trim(fMessage) = '' then Exit;
 New(ErrInfo);
 ErrInfo^.bstrSource := c_AddinName;
 ErrInfo^.bstrDescription := fMessage;
 ErrInfo^.wCode:=1006;
 iError.AddError(nil, ErrInfo);
end;


В программе 1С можно отловить эти сообщения конструкцией Попытка-Исключение-КонецПопытки, и тогда они показаны пользователю не будут (можно показать на экране другое, понятное пользователю, сообщение, а технические детали записать в журнал ошибок). Более того, это позволяет прервать обработку кода 1С и передать управление на оператор, следующий за ключевым словом Исключение (англоязычный синоним - Except). Например, если программа не может подключиться к почтовому серверу, то и дальнейшие действия по отправке почты бессмысленны, и делать их не нужно.

Приведу другой, менее очевидный, пример контроля ошибок. Пример "подопытного" кода делает следующее: преобразует 3 двоичных байта в 4 текстовых байта по алгоритму Base64. Подобное преобразование необходимо, например, при отправке почтовых вложений, поскольку сама электронная почта "понимает" только текст. Не претендуя на скорострельность самого алгоритма преобразования, обращу ваше внимание лишь на контроль ошибок, помеченный в коде комментарием с тремя звездочками:

////////////////////////////////////////////////
   procedure T_Base64.EncodeTriplet;
   var temp1, temp2: byte;
   begin
     if bytes>3 then error('Число байтов для кодирования превышает 3');  //***
     if bytes<1 then error('Число байтов для кодирования меньше 1'); //***

     if bytes<3 then b3:=0;
     if bytes<2 then b2:=0;

     s1:= (b1 shr 2) and $3F; //берем первые 6 бит первого байта и помещаем их в s1

     temp1:=(b1 shl 4) and $30; //берем последние 2 бита первого байта
     temp2:=(b2 shr 4) and $0F; //берем первые 4 бита второго байта
     s2:=temp1 or temp2; //складываем их вместе и получаем s2

     temp1:=(b2 shl 2) and $3C; //берем последние 4 бита второго байта
     temp2:=(b3 shr 6) and $03; //берем первые 2 бита третьего байта
     s3:=temp1 or temp2; //складываем их вместе и получаем s3

     s4:= (b3 and $3F); //берем последние 6 бит третьего байта и получаем s4

     if s1>63 then error('Ошибка кодирования байта 1'); //***
     if s2>63 then error('Ошибка кодирования байта 2'); //***
     if s3>63 then error('Ошибка кодирования байта 3'); //***
     if s4>63 then error('Ошибка кодирования байта 4'); //***

     c1:= base64ABC[s1+1];
     c2:= base64ABC[s2+1];
     c3:= base64ABC[s3+1];
     c4:= base64ABC[s4+1];

     if bytes<3 then c4:='=';
     if bytes<2 then c3:='=';
   end;

Что это за ошибки, контроль которых я произвожу в этом примере? Ну, казалось бы, кто будет подавать на вход подобного алгоритма меньше одного или больше 3 байт? И кто будет получать на выходе числа вне диапазона 0..63? Однако не спешите с выводами - подобные страховочные проверки "невозможных" ситуаций помогают находить "человеческие" ошибки программиста (лично я при разработке данного фрагмента кода "натыкался" на них пару-тройку раз), и предотвратить появление других, "наведенных" ошибок, которые могут оказаться значительно более сложными и дорогостоящими в их поиске и исправлении.
1 Прапорщик Задов
 
28.03.05
03:43
лень читать. замути в виде изложения. слов на 15-20
2 skunk
 
28.03.05
04:03
(0)а к чему все это?
3 Критик
 
28.03.05
04:11
Рома, первая часть почти шедевральна, я плакал над тезисом про ключи защиты. Пиши ещё, я уже задумался, чтобы выучить и как-то применять. Вторую, извини, надо оборачивать в жёлтую обложку; это для кого? я вот писал на асме под почти все моторы когда-то - мне не надо это, а кто по хх в час за формы сколачивает - у него ни времени, ни сил, ни ЖЕЛАНИЯ. Для студентов, жаждущих знаний, как раз если тока, для козерогов причём :); тебе надо преподавать пробовать, я серьёзно. Удачи
4 romix
 
28.03.05
04:38
(1) Там где перечисление во втором абзаце, имеет место акростих. Читаешь первые буквы каждой фразы. Это и есть краткое изложение. :-)

(2) Да вот книжку Джеральда Даррелла ("Сад богов") почитал, и тоже (в этом же стиле) решил написать о том, что меня беспокоит. :-) В частности, о дятлах. Кстати, очень рекомендую. :-)

(3) Тут не так все очевидно. Хотя про исключения и их программное возбУждение знают, наверное, все, но что их можно удобно юзать, юниксоиды, например, и вовсе не в курсе. :-) Я сам не сразу до этого допер. С исключениями оказалось гораздо удобнее и безглючнее кодить, чем с кодами возврата.
5 Стая дятлов
 
28.03.05
05:10
(0) Просьба не отождествлять нас и каждый упавший железнобетонный забор.
SP: все вопросы к кротам...
6 ДолойОшибок
 
28.03.05
06:19
(0) Тема очень интересная и нужная, но как бы заставить заниматься обработкой ошибок фирму "1С" ?
7 zzzzz
 
28.03.05
06:45
(4) Не помню, но на мой взгляд, попытка исключение занимает больше времени, чем проверка на условие. Так что в отлаженном коде их должно быть как можно меньше.
8 427
 
28.03.05
06:57
КГ/АМ....

Особенно умилила фраза
"- Информационный обмен, например, от кассы в программу или из программы в кассу "прогружает" не все данные. Это означает, что клиент (и его покупатели) будут щелкать лицом (или клювом) перед кассой, если нужный товар в кассу все-таки не пришел.
- Зависает или постоянно дает сбои компонента для электронных весов, когда в них посылают из компьютера информацию о товарах. Это особенно удобно, когда вам срочно нужно что-то взвешивать.


нехрен засовывать одно изделие под названием "Калькулятор буха" во все дырки подряд. Если у аффтарав нет денех на нормальный резиновый .... имитатор и они пользуются калькулятором для этих целей... не комментируется...
9 Антоха ORIGINAL
 
28.03.05
09:10
Ты не прафф 427-ой! Праграмма 1С - красивая праграмма, она нравица булгактирам за свою жёлтость... но не нравица праграмирам...
Ситуёвина сильно смахивает на дикую недоделку (впопыхах лабали) мелкого франча, взявшегося за малые бабки построить мега-супер-пупер ERP...
10 Asmody
 
28.03.05
10:48
(0) в БЗ по заголовком "Почему надо пользоваться оператором Попытка_исключнеи_КонецПопытки;"
11 romix
 
28.03.05
13:57
(7) Попытка и исключение отнимают время, но возбуждение (Raise) исключения
по условию времени отнимает не больше, чем само условие. Поэтому можно втыкать как можно больше, а потом, если надо, оптимизировать глубокие циклы... А Попытка-Исключение может быть одна на всю программу...
12 romix
 
28.03.05
14:01
(7) Попытка и исключение отнимают время, но возбуждение (Raise) исключения
по условию времени отнимает не больше, чем само условие. Поэтому можно втыкать как можно больше, а потом, если надо, оптимизировать глубокие циклы... А Попытка-Исключение может быть одна на всю программу...
13 romix
 
28.03.05
14:06
(8) Для обмена с кассами используется не 1С, а приблуда (служба) под названием SMS. Впрочем, непонятки с ней те же, что и с ВК.
14 И ещё
 
28.03.05
14:15
+13 такая гадость как АРМ кассира...
15 romix
 
28.03.05
14:47
(14) А че гадость - их же много щас на рынке...
Супермаг что ли под MS-DOS? Он вроде нормальный (если не считать проблемы с ком-портом на быстрых машинах).
16 miki
 
28.03.05
14:49
(13) А чё sms это не вк?
17 romix
 
28.03.05
14:52
(6) Microsoft заставили (линухом). :-)
(9) Не мелкого. А одного из лучших.
18 romix
 
28.03.05
14:54
(16) В конкретной реализации это служба. Накапливает данные о продажах в базе SQL и позволяет делать красивые отчеты, а заодно служит транспортом (правда, кривым и через пень-колоду) между 1С и кассами.
19 Кроты
 
28.03.05
15:20
(5) Кроты - полезные животные!

Мы, также как и дятлы, едим личинок (и только личинок - корни растений, даже перед страхом голодной смерти, не жрем, и столбы не грызем).

Так что мы тоже санитары, так сказать, леса... Тока не долбим древесину, а копаем землю... Это тоже тяжелый труд...
20 ХЗ
 
28.03.05
15:38
, конечно. Но тема не отражает суть сообщения.
21 А кто
 
28.03.05
15:39
(19)огроды раззоряет!? Жаль все кротоловки в деревне оставил...
22 Кроты
 
28.03.05
18:20
(21)
Всякие медведки и проволочники жрут корни, а кроты, наоборот, жрут всяких личинок, которые жрут корни.

(хотя для этого приходится немножко покопать).
23 romix
 
29.03.05
15:55
Поправка: указанный в статье код

 except
   on E: Exception do ShowErrorLog(E.Message);
 end;
 CallAsFunc := S_OK;

работает не совсем верно (генерит исключения только первые 5 раз). 24 релиз.
С чем это связано, я не знаю. То есть, Попытка-Исключение то работает, то нет.

Микрохирургия глаза показала, что при ошибке надо возвращать значение
             CallAsFunc := E_FAIL;
и тогда исключение во всех случаях генерируется нормально.
Может быть, есть более правильные решения, не знаю.