Форум: "Начинающим";
Текущий архив: 2011.02.20;
Скачать: [xml.tar.bz2];
ВнизЛоггирование исключений с последующим их перевозбуждением. Найти похожие ветки
← →
>| (2010-11-26 16:33) [0]Прошу мастеров просмотреть данный код на предмет подводных камней.
Будет ли он правильно работать и какие могут быть нюансы.procedure TDMMain.KMSExceptionHandler(Sender: TObject; E: Exception);
var fn:string;
LOB: TLOBLocator;
MyBlobStream: TMemoryStream;
ComponentName:string;
begin
try
try
Fn := ExtractFilename(Application.ExeName)+"_"+
Screen.ActiveForm.Name+
FormatDateTime("_ddmmyyyy_hhnnss",now)+
"_debug";
MyBlobStream:= TMemoryStream.Create;
DoGenerateScreenshot;
LOB:= TLOBLocator.CreateTemporary(DMMain.OracleSession, otBLOB, True);
try
oqInsertError.ClearVariables;
oqInsertError.SetVariable("V_FORM_NAME",Screen.ActiveForm.Name+":"+Screen.ActiveForm.Caption);
oqInsertError.SetVariable("V_ERROR_TEXT",E.Message);
oqInsertError.SetVariable("V_ID_USER",DMMain.UserID);
oqInsertError.SetVariable("V_DATETIME",Now);
oqInsertError.SetVariable("V_OSUSER_NAME",CollectUserName);
oqInsertError.SetVariable("V_COMP_NAME",CollectComputerName);
oqInsertError.SetVariable("v_exception_name", E.ClassName);
if Sender is TComponent then
ComponentName := TComponent(Sender).Name
//TODO: Попытаться вытянуть из сендера текстовое заглавие компонента, который возбудил ошибку
else
ComponentName := "<имя неизвестно>";
oqInsertError.SetVariable("v_object_name",Sender.ClassName+":"+ ComponentName);
MyBlobStream.Position := 0;
LOB.CopyFrom(MyBlobStream, MyBlobStream.Size);
oqInsertError.SetComplexVariable("V_SCREENSHOT", LOB);
oqInsertError.Execute;
DMMain.OracleSession.Commit;
finally
LOB.Free;
MyBlobStream.Free;
end;
except
on E_local:Exception do
begin
raise E_local.Create("Ошибка логгирования:"#10#13+ E_local.Message);
MyBlobStream.SaveToFile(fn+".jpg");
end;
end;
finally
Application.OnException := nil;
raise E.Create("Произошло исключение, которое было записано в БД.");
Application.OnException := KMSExceptionHandler;
end;
end;
← →
Rouse_ © (2010-11-26 16:42) [1]вот это работать не будет:
raise E.Create("Произошло исключение, которое было записано в БД.");
Application.OnException := KMSExceptionHandler; <<< вызвано не будет
← →
Rouse_ © (2010-11-26 16:42) [2]ну и вот это тоже :)
MyBlobStream.SaveToFile(fn+".jpg");
← →
Юрий Зотов © (2010-11-26 16:43) [3]Весь код не смотрел, но вот здесь 2 ошибки:
finally
Application.OnException := nil;
raise E.Create(...); // Е - это не класс, а экземпляр объекта.
Application.OnException := ... // Эта строка никогда не выполнится
end;
← →
Сергей М. © (2010-11-26 16:44) [4]
> на предмет подводных камней
Главый камень - вся эта петрушка не будет работать для необработанных исключений, возбужденных в потоках, отличных от основного.
← →
Rouse_ © (2010-11-26 16:47) [5]Ну и кстати да: весь код нитей в собственный глухой try except завернут, зачем не понятно...
← →
Сергей М. © (2010-11-26 16:50) [6]
> Rouse_ © (26.11.10 16:47) [5]
> весь код нитей в собственный глухой try except завернут,
> зачем не понятно
Если ты о TThread, то оч даже понятно - для формошлепов это сделано)
← →
>| (2010-11-26 17:14) [7]
> Весь код не смотрел, но вот здесь 2 ошибки:
>
> finally
> Application.OnException := nil;
> raise E.Create(...); // Е - это не класс, а экземпляр
> объекта.
> Application.OnException := ... // Эта строка никогда не
> выполнится
> end;
в принципе, это ключевое место
тогда вопрос, как перевозбудить исключение, которое содержится в объекте E?
Можно ли написать следующим образом:
finally
Application.OnException := nil;
try
raise // каким образом здесь перевозбудить исключение?
finally
Application.OnException := KMSExceptionHandler;
end;
end;
> Главый камень - вся эта петрушка не будет работать для необработанных
> исключений, возбужденных в потоках, отличных от основного.
>
Таки да, в приложении будут работать потоки, для которых будет своя обработка.
Цитирую:
"При использовании TThread у вас уже есть возможности для манёвра. Дело в том, что функция потока в TThread оборачивает вызов Execute в try/except. Любое исключение, сбежавшее из Execute, заносится в свойство FatalException потока. Исключение будет удалено в деструкторе TThread. А вы можете проанализировать этот код из события OnTerminate или из кода главного потока, например:
T := TMyThread.Create(False);
try
// ... <- Выполняем какие-то действия. Поток в это время также выполняет работу.
// Мы свою работу выполнили - ждём конца работы потока.
T.WaitFor;
// Анализируем: успешно ли завершился поток?
if T.FatalException <> nil then // есть ошибка
begin
// Обработка ошибки потока.
end;
finally
FreeAndNil(T);
end;"
← →
>| (2010-11-26 17:16) [8]Вопрос остается открытым:
Каким образом перевозбудить исключение после записи его в лог?
← →
>| (2010-11-26 17:20) [9]Можно ли таким образом поступить?
finally
Application.OnException := nil;
try
raise E;
finally
Application.OnException := KMSExceptionHandler;
end;
end;
← →
Сергей М. © (2010-11-26 17:28) [10]
> Можно ли таким образом поступить?
> finally
> Application.OnException := nil;
> try
> raise E;
> finally
> Application.OnException := KMSExceptionHandler;
> end;
> end;
Непонятно зачем эти пляски с бубном вокруг обниления и восстановления обработчика Application.OnException в теле самого обработчика ..
← →
>| (2010-11-26 17:36) [11]
> Непонятно зачем эти пляски с бубном вокруг обниления и восстановления
> обработчика Application.OnException в теле самого обработчика
> ..
Капитан Очевидность спешит с ответом:
Для логирования ошибок:-)
← →
>| (2010-11-26 17:39) [12]Если не выключать обработчик, полагаю, возникнет циклическое исключение.
← →
Сергей М. © (2010-11-26 17:40) [13]Ну и логируй их себе на здоровье)
Зачем обнилять-то обработчик и потом восстанавливать ?
Или ты настольуо не уверен в своих телодвижениях, что боишься допуститиь в теле обработчика непредвиденное исключение ?)
← →
clickmaker © (2010-11-26 17:40) [14]мне тоже непонятно...
Log(E_local.Message);
raise E_local;
и всё
← →
Rouse_ © (2010-11-26 17:41) [15]местами строчки поменять и всего делов, но все равно не понятно, зачем все так сложно? Возьми джеди - там хороший обработчик SEH фреймов имеется.
← →
Сергей М. © (2010-11-26 17:45) [16]
> Если не выключать обработчик, полагаю, возникнет циклическое
> исключение
А зачем его перевозбуждать-то ?
← →
Юрий Зотов © (2010-11-26 17:53) [17]
> >|< (26.11.10 17:16) [8]
> Каким образом перевозбудить исключение после записи его
> в лог?
Например, так:type
EMyException = class(Exception)
public
constructor Create(E: Exception);
end;
constructor EMyException.Create(E: Exception);
begin
inherited Create(
Format("Произошло и записано в БД исключение класса %s: %s",
[E.ClassName, E.Message])
end;
Вызов такой:Application.OnException := nil;
try
raise EMyException.Create(E)
finally
Application.OnException := KMSExceptionHandler;
end;
Но вместо всех этих плясок с перевозбуждением исключения можно просто вызвать ShowMessage и выдать сообщение:Format("Произошло и записано в БД исключение класса %s: %s", E.ClassName, E.Message])
← →
KSergey © (2010-11-26 17:59) [18]> clickmaker © (26.11.10 17:40) [14]
> мне тоже непонятно...
Автор опасается, что исключение, возникшее в Application.OnException приведет к вызову того же Application.OnException, где опять исключение - и по кругу.
Прав ли он - надо в код VCL смотреть или натурный эксперимент ставить.
← →
Сергей М. © (2010-11-26 18:03) [19]
> KSergey © (26.11.10 17:59) [18]
> Прав ли он
Прав.
Но перевозбуждать (или возбуждать новое) исключение в обработчике, СПЕЦИАЛЬНО предназначенном для обработки необработанных исключений, - это ничем не оправданная блажь.
Чтобы уронить свой процесс великого ума не требуется)
← →
KSergey © (2010-11-26 18:09) [20]> Сергей М. © (26.11.10 18:03) [19]
Ну тогда надо делать как обычно: код обработки исключений не должен исключения генерировать
Т.е. тупо try/except по краям в него - и все.
Как вариант - в except вставить диалоговое окно "была ошибка, но сохранить в базу не сумели, делайте скриншот". Если уж диалоговое окно вызовет исключение - ну это явно совсем не повезло юзеру, на карму его и свалить :)
← →
KSergey © (2010-11-26 18:11) [21]К стати, а зачем именно исключение возбуждать с текстом "Произошло исключение, которое было записано в БД."? достаточно это в виде диалогового окна отобразить - да и все. Ну раз уж взяли на себя работу стандартного обработчика - ее надо делать до конца.
← →
>| (2010-11-26 18:14) [22]В чем идея? Идея не в том, чтобы пользователь пугался страшных окон с ошибками и не знал куда бежать. Мне не нужно выводить универсальное окно с понятными словами.
Идея даже не в том, чтобы погасить все исключения.
Это неприемлемо, поскольку произойдет ошибка, а пользер будет и дальше работать как ни в чем не бывало.
Идея состояла только в том, чтобы вклиниться в очередь возникновения ошибок и перед ее возникновением отправить скриншот(другие данные) в БД, чтобы можно было проанализировать причину.
Вот для этого я и перевозбуждаю его, чтобы пользер не думал, что все в порядке.
← →
Сергей М. © (2010-11-26 18:18) [23]все что нужно - это
//логируем исключение,
//код логгера должен гарантировать невозможность возбуждения в нем необраб.иск-й
Log(..);
//покажем юзеру, если так уж надо, оригинальное исключение
ShowException(E);
← →
>| (2010-11-26 18:19) [24]Возможно, дальше будет анализ типов исключений и в зависимости от их значений гасить или исправлять ситуацию вручную.
Например, если приложение было включено целую ночь, но к базе не было обращений, то коннект обрывается.
В этом случае, если возникнет, исключение "Session Undefined", просто переподключиться к БД.
← →
Сергей М. © (2010-11-26 18:26) [25]
> для этого я и перевозбуждаю его, чтобы пользер не думал,
> что все в порядке
А он и не подумает что все в порядке)
Как бы ты не вуалировал текст оригин.сообщения об исключении, для юзера он все равно будет матерными словами.
Почему бы не выдать нечто вида "Возникла непредвиденная ошибка. Выполнение программы будет аварийно прервано, с претензиями обратитесь конкретно туда-то" ?
Ведь если искл-е не было предвидено разработчиком, дальнейшее поведение программы чаще всего будет непредсказуемым и очередные искл-я посыпятся в лог и на экран как из рога изобилия. Разобираться потом в этой куче намного сложнее чем в одном-единственном исключении, которое юзер при предъяве претензий передал содержимым протокола.
← →
>| (2010-11-26 18:47) [26]
> Сергей М. © (26.11.10 18:26) [25]
В общем, Вы меня убедили.
Я отказался от перевозбуждения исключений.
Всем спасибо за помощь и внимание к моему вопросу.)
← →
_Юрий (2010-11-26 18:52) [27]Совсем гасить текст оригинального исключения - тоже нехорошо, потому что может оказаться так, что пользователь в состоянии самостоятельно разрулить проблему, не дергая саппорт.
например, если в настройках путь к БД неверный, вывод даже сообщения типа
"Cannot open file c:\myPath\MyFile.db" может оказаться полезным.
← →
>| (2010-11-26 18:56) [28]
> Совсем гасить текст оригинального исключения - тоже нехорошо,
> потому что может оказаться так, что пользователь в состоянии
> самостоятельно разрулить проблему, не дергая саппорт.
> например, если в настройках путь к БД неверный, вывод даже
> сообщения типа
> "Cannot open file c:\myPath\MyFile.db" может оказаться
> полезным.
По этому поводу буду выводить следующее сообщение:
ErrorDlg("Возникла ошибка:"+#10#13+E.Message+
#10#13+"Для ее устранения обратитесь к разработчикам."+
#10#13+"Желательно повторить действия, которые привели к ее возникновению и"+
#10#13+"убедиться, что причина ошибки определена.");
← →
_Юрий (2010-11-26 19:07) [29]
> >|< (26.11.10 18:56) [28]
кстати начиная с 2009 наконец то появились InnerException, что позволяет при определенных усилиях получить весь "логический стек" ,начиная с момента возникновения ошибки.
Если используется такая версия, рекомендую посмотреть статью:
http://www.gunsmoker.ru/2010/04/exception-delphi-2009.html
← →
KSergey © (2010-11-26 21:49) [30]> >|< (26.11.10 18:14) [22]
> Идея даже не в том, чтобы погасить все исключения.
> Это неприемлемо, поскольку произойдет ошибка, а пользер
> будет и дальше работать как ни в чем не бывало.
>
> Вот для этого я и перевозбуждаю его, чтобы пользер не думал, что все в порядке.
Я понял в чем ошибка вашего миропонимания. Вы просто перевозбудились! (сугубо каламбур, ничего личного)
Ваше миропонимание с "перевозбуждением" (исключений) базируется на том, что вы в обычном Windows дельфи-приложении всегда имеете защитную оболочку в виде перехватчика всех исключений, который выводит окно с информцией, глушит исключение, после чего цикл выборки Windows-сообщений продолжается.
Однако Application.OnException - это как раз и есть та самая последняя подушка, которая собственно эти исключения перехватывает. Ну т.е. перехватывает, конечно, блок try/exception вокруг кода обработки собщения, но при задании пользователем обработчика для Application.OnException собственно этому обработчику и отдается на откуп вся логика по обработке факта возникновения исключения.
Но давайте от словоблудия- к коду.
Вот что есть с VCl:procedure TApplication.Run;
begin
FRunning := True;
try
AddExitProc(DoneApplication);
if FMainForm <> nil then
begin
case CmdShow of
SW_SHOWMINNOACTIVE: FMainForm.FWindowState := wsMinimized;
SW_SHOWMAXIMIZED: MainForm.WindowState := wsMaximized;
end;
if FShowMainForm then
if FMainForm.FWindowState = wsMinimized then
Minimize else
FMainForm.Visible := True;
repeat
try
HandleMessage;
except
HandleException(Self);
end;
until Terminated;
end;
finally
FRunning := False;
end;
end;
Это как раз тот метод объекта Application, который тупо крутит цикл выборки сообщений repeat/until все время существования Windows-приложения.
Обратите внимание на выделенный фрагмент. Именно внутри метода HandleMessage выполняются решительно весь тот код, который вы собственно и пишите. Потому как любой код есть лишь реакция на какое-либо сообщение. Ни в какое другое место (кроме Initialization/finalization и событий создение/уничтожение главной формы) свой код вы внедрить не можете. Только внутрь обработчика какого-либо сообщений Windows.
И что же мы видим? а мы видим, что все необработанные исключения здесь тупо гасятся, при этом вызывается HandleException, который весьма прост:procedure TApplication.HandleException(Sender: TObject);
begin
if GetCapture <> 0 then SendMessage(GetCapture, WM_CANCELMODE, 0, 0);
if ExceptObject is Exception then
begin
if not (ExceptObject is EAbort) then
if Assigned(FOnException) then
FOnException(Sender, Exception(ExceptObject))
else
ShowException(Exception(ExceptObject));
end else
SysUtils.ShowException(ExceptObject, ExceptAddr);
end;
Что же мы видим в выделеном фрагменте? если установлен свой обработчки исключений для приложения - то вызовется он. Если не установлен - вызовется просто отображение информационного окна. Все! Т.е. перевозбуждать в том месте исключение просто не имеет смысла, его попросту некому будет обработать! дальше только run-time error и принудительное завершение процесса.
Так что все, что вы можете здесь сделать - это сохранить лог и отобразить что-то пользователю. Причем по-хорошему исключения из OnException выпускать наружу нельзя. (Проверьте что будет, если внутри OnException возбудить исключение; я думаю прога тут же завершится с "программа выполнила недопустимую операцию и будет закрыта", проверять лень).
Надеюсь, объяснил понятно.
← →
Leonid Troyanovsky © (2010-11-28 22:27) [31]
> >|< (26.11.10 18:14) [22]
> Это неприемлемо, поскольку произойдет ошибка, а пользер
> будет и дальше работать как ни в чем не бывало.
Хорошее имя - пользер. Записал себе в мемо.
> KSergey © (26.11.10 21:49) [30]
Про перевозбуждение тоже понравилось.
--
Regards, LVT.
← →
_oxffff (2010-11-29 08:48) [32]
> в принципе, это ключевое место
> тогда вопрос, как перевозбудить исключение, которое содержится
> в объекте E?
Reraising ExceptionsFrom RAD Studio
Go Up to Exception handling Index
Sometimes when you handle an exception locally, you want to augment the handling in the enclosing block, rather than replace it. Of course, when your local handler finishes its handling, it destroys the exception instance, so the enclosing block"s handler never gets to act. You can, however, prevent the handler from destroying the exception, giving the enclosing handler a chance to respond. You do this by using the raise command with no arguments. This is called reraising or rethrowing the exception. The following example illustrates this technique:
try
{ statements }
try
{ special statements }
except
on ESomething do
begin
{ handling for only the special statements }
raise;{ reraise the exception }
end;
end;
except
on ESomething do ...;{ handling you want in all cases }
end;
← →
_oxffff (2010-11-29 09:29) [33]Также в новых версиях есть поддержка вложенных исключений
см. Exception.InnerException (specifies the inner exception)
Read the value of the InnerException property to obtain the inner exception. If there are no exceptions that triggered the current one, InnerException returns nil; otherwise InnerException returns the inner exception that triggered a specific exception.
← →
Дмитрий Белькевич (2010-11-29 11:03) [34]
> Идея состояла только в том, чтобы вклиниться в очередь возникновения
> ошибок и перед ее возникновением отправить скриншот(другие
> данные) в БД, чтобы можно было проанализировать причину.
>
Можно посмотреть в сторону эврикалог еще. Сильно помогает, делает то, что написал и еще много всяких вкусностей, типа снятия стёка процедур.
← →
Ega23 © (2010-11-29 12:18) [35]
> По этому поводу буду выводить следующее сообщение:
> ErrorDlg("Возникла ошибка:"+#10#13+E.Message+
Не вдаваясь в подробности всей ветки и разбирание самого вопроса, хочу спросить: а почему #10#13?
$0D - конец строки
$0A - возврат каретки
Или я не прав?
← →
Anatoly Podgoretsky © (2010-11-29 12:26) [36]> Ega23 (29.11.2010 12:18:35) [35]
Ну наверно автор решил разместить их по возрастанию и это он сделал зря.
← →
Ega23 © (2010-11-29 12:28) [37]
> Ну наверно автор решил разместить их по возрастанию и это
> он сделал зря.
Я просто такой код настолько часто вижу, что уже начинаю сомневаться, что #13#10 - правильный вариант.
← →
Anatoly Podgoretsky © (2010-11-29 13:52) [38]> Ega23 (29.11.2010 12:28:37) [37]
Он правильный (#10#13), но только хакерский
А #13#10 это стандартный разделитель ДОС/Виндоус
← →
clickmaker © (2010-11-29 14:28) [39]> Он правильный (#10#13), но только хакерский
дядя Толя, поделился бы своим опытом бывшего хакера. )
почему именно "хакерский" я до сих пор не понимаю
← →
Anatoly Podgoretsky © (2010-11-29 19:45) [40]Чего тут делиться, есть три стандартных разделителя, в зависимости от ОС
CR, LF, CRLF
Но хакерам хотелось быть особыми, не похожемы на других, вот они и предумали четвертую комбинацию, которая не признается ни одной ОС - LFCR
Вот и весь секрет.
← →
Leonid Troyanovsky © (2010-11-29 21:44) [41]
> Anatoly Podgoretsky © (29.11.10 19:45) [40]
> Вот и весь секрет.
Перепутывание от перевозбуждения.
--
Regards, LVT.
← →
Игорь Шевченко © (2010-11-29 23:55) [42]
> Вот и весь секрет.
только вот мессадж-боксам это по барабану. Эти разделители имеют смысл в файлах :)unit main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
Button6: TButton;
Button7: TButton;
Button8: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button8Click(Sender: TObject);
procedure Button7Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(#10+#13+"foo"+#10+#13+"bar");
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage(#13+#10+"foo"+#13+#10+"bar");
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
ShowMessage(#13+"foo"+#13+"bar");
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
ShowMessage(#10+"foo"+#10+"bar");
end;
procedure TForm1.Button5Click(Sender: TObject);
begin
MessageBox (0, PChar(#10+#13+"foo"+#10+#13+"bar"), "FOO", MB_OK);
end;
procedure TForm1.Button6Click(Sender: TObject);
begin
MessageBox (0, PChar(#13+#10+"foo"+#13+#10+"bar"), "FOO", MB_OK);
end;
procedure TForm1.Button7Click(Sender: TObject);
begin
MessageBox (0, PChar(#13+"foo"+#13+"bar"), "FOO", MB_OK);
end;
procedure TForm1.Button8Click(Sender: TObject);
begin
MessageBox (0, PChar(#10+"foo"+#10+"bar"), "FOO", MB_OK);
end;
end.
← →
Inovet © (2010-11-30 00:21) [43]> [42] Игорь Шевченко © (29.11.10 23:55)
> только вот мессадж-боксам это по барабану
Странно, всегда пользуюсь разделителями, 0x0a который, в MessageBox, и новая строка получается.
← →
Anatoly Podgoretsky © (2010-11-30 00:24) [44]Извращений много разных, вот и Inovet такой же извращенец, наверно его жизнь не била, но все впереди.
← →
Anatoly Podgoretsky © (2010-11-30 00:25) [45]Вообще то правильнее использовать LineBreak
← →
Inovet © (2010-11-30 00:41) [46]> [44] Anatoly Podgoretsky © (30.11.10 00:24)
> вот и Inovet такой же извращенец,
> [45] Anatoly Podgoretsky © (30.11.10 00:25)
> Вообще то правильнее использовать LineBreak
Путаю я эти коды в цифрах, вот такой вобщем и изпользую "\n" - новая строка, а код его таки 0x0a.
> [38] Anatoly Podgoretsky © (29.11.10 13:52)
> А #13#10 это стандартный разделитель ДОС/Виндоус
Блин, открыл SysUtils.pas посдледовательность 0D 0A в конце строк.
← →
Anatoly Podgoretsky © (2010-11-30 00:43) [47]> Inovet (30.11.2010 00:41:46) [46]
LineBreak и путаться не будешь, Дельфи сама будет учитывать ОС
Рекомендую справку взглянуть, если она есть.
← →
Inovet © (2010-11-30 00:55) [48]> [47] Anatoly Podgoretsky © (30.11.10 00:43)
> Рекомендую справку взглянуть
Угу, так.
Из MSDN о MessageBox
lpText
[in] Pointer to a null-terminated string that contains the message to be displayed. If the string consists of more than one line, you can separate the lines using a carriage return and/or linefeed character between each line.
Так что нет изварещения
> [46] Inovet © (30.11.10 00:41)
> открыл SysUtils.pas посдледовательность 0D 0A в конце строк.
В смысле открыл Hex вьювере, на концы строк посмотреть.
← →
Inovet © (2010-11-30 01:11) [49]> [46] Inovet © (30.11.10 00:41)
> > [38] Anatoly Podgoretsky © (29.11.10 13:52)
> > А #13#10 это стандартный разделитель ДОС/Виндоус
>
> 0D 0A в конце строк.
Ты же это самое и написал.
Кстати, у меня как запомнилось когда-то на слух "carriage return linefeed", так и осталось, а коды, как коснётся приходится вспоминать какой из них какой, раньше помнил, но всё равно заменял символьным сочетанием.
← →
Германн © (2010-11-30 01:41) [50]
> Кстати, у меня как запомнилось когда-то на слух "carriage
> return linefeed", так и осталось, а коды, как коснётся приходится
> вспоминать какой из них какой
Так тебе АП уже несколько раз сказал "используй LineBreak"!
LineBreak - это константа в Дельфи (если ты еще этого не понял :)
← →
Inovet © (2010-11-30 02:18) [51]> [50] Германн © (30.11.10 01:41)
> LineBreak - это константа в Дельфи (если ты еще этого не
> понял :)
Я то понял, но не только же Делфи на свете.
← →
Германн © (2010-11-30 02:37) [52]
> Я то понял, но не только же Делфи на свете.
Да? Ну и кто же ещё существует на свете? :)
А уж на ДМ просто неприлично упоминать что-то, что не Дельфи. :)
← →
Anatoly Podgoretsky © (2010-11-30 09:08) [53]
> Я то понял, но не только же Делфи на свете.
Я поганой метлой вымету всех отсюда, кто не с Дельфи.
Страницы: 1 2 вся ветка
Форум: "Начинающим";
Текущий архив: 2011.02.20;
Скачать: [xml.tar.bz2];
Память: 0.63 MB
Время: 0.005 c