Привет, народ ! Может, это очень просто на самом деле. :) Вылетает сообщение "List index out of bounds (-1)" при работе с tStrings. Собственно, в имею стандартную ситуацию:
begin
// ставил здесь FreeAndNil(FProjects); ставил так же FProjects.Destroy; - не помогает; FProjects.Delete(0) тоже.
//FProjects здесь из одной строки, FProjects[0]='10' (10 - строка) FProjects:=TStringList.Create; //этот самый действенный метод тоже не работает.. вылетает с сообщением Estring Error with Message 'List index out of bounds (-1)'
//вобщем, надо сделать пустой FProjects
end;
До этого - создание FProjects:
FProjects:=Tstringlist.Create;
If FileExists(FProjects_file)=true then
FProjects.LoadFromFile(FProjects_file) //но файла нет
else
FProjects.Add('10'); //собственно, у меня так и доходит
Далее, по ситуации сразу приходит к стандартной задаче, описанной в начале.
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
28-10-2008 15:54 | Комментарий к предыдущим ответам
Кстати, если читать вдумчиво, то правильный ответ можно было привести ещё сразу после сообщения от 20-10-2008 01:11, где вы привели этот код с while.
Но когда код даётся такими короткими отрывками, надо быть сильно глазастым, чтобы найти поблему. Когда код целиком перед глазами, можно мысленно по нему пройтись и подумать.
"Так вот: конструктор TStringList не может возбудить EStringListError. Почему? Потому что у него нет своего конструктора, и конструктор наследуется напрямую от TObject. А конструктор у TObject ничего не делает. Это значит, что конструктор TStringList пустой! Вы и сами можете убедиться в этом: щёлкните мышью с зажатым Ctrl по слову Create в строке с TStringList.Create. Пустой конструктор, очевидно, не может возбуждать EStringListError." - буду иметь в виду.
"Поэтому, после таких, заведомо ложных, утверждений, я весьма скептически отношусь к любым вашим словам о том, что что-то возникло где-то. Тем более, что вы не говорите, как вы это определяете(?!).
Окей, как я тогда могу утверждать, что исключение у вас возбуждается в while? Давайте посмотрим.
Потому что вы привели стек вызовов - это надёжная информация, полученная из отладчика. Не могли же вы её придумать, в конце концов (если только, конечно, вы не хотите всех запутать)." Хф) даже не думал.
[спокойно сижу слушаю]
[Несмотря на то, что вы так и не удосужились ответить на вопрос: что будет, если щёлкнуть в нём по LoadFProjectsList?]
Sorry. У меня руки до этого не дошли (уже изменил код по-дедовски, как вам говорил когда-то)..
[спокойно сижу и слушаю]
"Примечание: на самом деле, у меня ещё есть сомнения по поводу этой строчки, т.к. у вас были метания по поводу того, стоит ли 0 или 1 в условии перед ней." ХМ! Но на самом деле, там у меня, слава богу, комментарий к нему, и он красноречиво говорит об Count>1 ! Привык ставить условие Count>0 (так чаще приходится), как программер. То есть не более, чем очепятка.
[слушаю дальше]
[пожимаю вам руку].
Спасибо, что нашёлся хоть один человек, кто мне обьяснил ! С толку сбило то, что вылетало на этой строке, при любой команде очистки переменной tStrings. Искал только до этой строки причину).
>>> а ничего, что до строчки с этим While и не доходит ?
Эээээ... а это вы определили тем же способом, что и то, в какой строчке у вас вылетает?
Давайте ещё раз:
FProjects:=TStringList.Create; // вылетает с сообщением Estring Error with Message 'List index out of bounds (-1)'
Это ваши слова или нет?
Так вот: конструктор TStringList не может возбудить EStringListError. Почему? Потому что у него нет своего конструктора, и конструктор наследуется напрямую от TObject. А конструктор у TObject ничего не делает. Это значит, что конструктор TStringList пустой! Вы и сами можете убедиться в этом: щёлкните мышью с зажатым Ctrl по слову Create в строке с TStringList.Create. Пустой конструктор, очевидно, не может возбуждать EStringListError.
Поэтому, после таких, заведомо ложных, утверждений, я весьма скептически отношусь к любым вашим словам о том, что что-то возникло где-то. Тем более, что вы не говорите, как вы это определяете(?!).
Окей, как я тогда могу утверждать, что исключение у вас возбуждается в while? Давайте посмотрим.
Потому что вы привели стек вызовов - это надёжная информация, полученная из отладчика. Не могли же вы её придумать, в конце концов (если только, конечно, вы не хотите всех запутать). Несмотря на то, что вы так и не удосужились ответить на вопрос: что будет, если щёлкнуть в нём по LoadFProjectsList? Из стека вызовов, тем не менее, можно вынести следующие выводы:
1). Исключение возбуждается в коде LoadFProjectsList, т.к. эта процедура есть в стеке вызовов. Это значит, что про остальной код можно забыть.
2). Более того, исключение возбуждается классом TStringList, т.к. в стеке вызовов есть TStringList.Get(-1). Это значит, что из кода LoadFProjectsList можно забыть про любой код, который не работает с TStringList.
3). Между LoadFProjectsList и TStringList.Get(-1) в стеке вызовов нет других вызовов процедур. Это значит, что метод TStringList.Get(-1) был вызван из LoadFProjectsList напрямую, а не из другого метода или процедуры. Если учесть, Get - это protected метод и является read-методом для свойства Strings, то получим, что исключение было возбуждено, когда мы обращались к строкам объекта класса TStringList прямо в LoadFProjectsList, например: FProjects[0].
Примечание: в отличие от других выводов, этот вывод может оказателься неверным, т.к. стек вызовов может пропускать вызовы процедур. Включением соответствующих опций мы снизили такую возможность, но полностью со счетов её сбрасывать не стоит.
4). Добавим теперь информацию, полученную из исходного кода LoadFProjectsList, можно взглянуть на все места, где у нас есть обращение к строкам:
Procedure LoadFProjectsList;
var i:Integer;
begin
if FProjects.Count=0 then
exit;
i:=Round(Form1.String_to_FloatNum(FProjects[0])); // Здесь
If FProjects.Count>1 then
FProjects.Delete(0) // Здесь
else
FProjects:=Classes.TStringList.Create;
While FProjects[FProjects.Count-1]='' do // Здесь
FProjects.Delete(FProjects.Count-1); // Здесь
While FProjects.Count > i do
FProjects.Delete(FProjects.Count-1); // И вот здесь
While FProjects.Count < i do
FProjects.Add('');
end;
Примечание: если отбросить пункт 3, то исключение также могло возникнуть в FProjects.Add(''). Почему это так - я оставлю вам в качестве домашнего задания.
5). Итак, мы отметили 5 потенциальных мест для возникновения исключения. Первые две строки нам не подходят по двум причинам:
а). Они обращаются к нулевому элементу, а в сообщении об ошибке у нас индекс равен -1.
б). Оператор if FProjects.Count=0 then exit в начале процедуры гарантирует наличие минимум одного (нулевого) элемента.
6). Следующая строка с "while FProjects[FProjects.Count-1]='' do". Сначала заметим, что исключение с индексом -1 может возникать в этой строке, если FProjects.Count будет равен 0. Давайте посмотрим, может ли FProjects.Count быть равен 0. Выполнение до этой строки может дойти по двум путям:
а). "FProjects.Delete(0);" - эта ветка кода не может привести к FProjects.Count = 0, т.к. условие If FProjects.Count>1 then гарантирует нам наличие двух строк в списке. После удаления одной из них у нас в списке будет минимум одна строка => FProjects.Count - не ноль.
Примечание: на самом деле, у меня ещё есть сомнения по поводу этой строчки, т.к. у вас были метания по поводу того, стоит ли 0 или 1 в условии перед ней.
б). "FProjects:=Classes.TStringList.Create;" - очевидно, что после выполнения этой строки FProjects.Count будет равен нулю, т.к. свежесозданный список всегда изначально пуст. Это приведёт к EStringListError в строке с while. Вот вам и путь для ошибки.
7). Итак, прав я или нет, зависит от того, какой путь проходит программа: по Delete или по пересозданию списка. Даже, если я не прав, это будет означать, что ошибка возникает в строчках ниже (у нас остались неразобранными ещё две строки). Тогда почему же я ткнул именно на эту строчку, а не ниже? А вот почему:
>>> Главное, всё волшебным образом исполняется, если убрать одно простое выражение в следующей процедуре:
Procedure LoadFProjectsFile;
begin
FProjects:=Classes.Tstringlist.Create;
If FileExists(FProjects_file) then
...
else
//FProjects.Add('zxcv'); //если его убрать, то всё ok. А так вылетает при любом добавлении string-типа
end;
Откуда следует, что программа проходит по пути с else. Что происходит, когда у нас строчка не закомментарена? Произойдёт вот что: мы создали пустой список и добавили в него 1 строчку. LoadFProjectsFile стоит сразу перед LoadFProjectsList - значит мы попадаем в LoadFProjectsList с одной строкой в списке. Это значит, что "If FProjects.Count>1 then" будет ложно, и мы пойдём по пути, описанному в пункте 6б - вот вам и путь выполнения программы до исключения.
Ну, доказал я свою точку зрения или нет? :)
Примечание: пунктов 4-7 можно было избежать, если бы вы сразу щёлкнули по LoadFProjectsList - тогда редактор кода встал бы на while сразу. 4-7 нужно выполнять только, если стек вызовов не сумел ничего показать или не сработал (не встал на строчку). Кроме того, можно было не выполнять эти выкладки в уме, а поставить бряк в начало LoadFProjectsList и пройти её по шагам.
21-10-2008 06:01 | Комментарий к предыдущим ответам
>>> По крайней мере, у меня вылетало на другой строке.
Не, вы извините, конечно, но вы вообще не смогли определить, где у вас вылетает.
Вот ваши слова:
1. >>> FProjects.Clear тоже вылетает с ошибкой 'List index out of bounds (-1).
Не бывает для стандартного TStringList без обработчиков.
2. >>> FProjects:=TStringList.Create; // вылетает с сообщением Estring Error with Message 'List index out of bounds (-1)'
Не бывает для стандартного TStringList.
В эти строчки формируют условия, при которых у вас происходит вылет. Но сам вылет происходит в while. В нём же сидит и причина.
А LoadFProjectsFile здесь и вовсе не при чём - забудьте про него. У вас в LoadFProjectsList стоит очистка списка, а сразу за ней - "while FProjects[FProjects.Count-1] = '' do".
Procedure LoadFProjectsFile;
begin
FProjects:=Classes.Tstringlist.Create;
If FileExists(FProjects_file)=true then
FProjects.LoadFromFile(FProjects_file)
else
begin
Form1.Console_Message('Список предыдущих проектов пуст');
//ShowMessage(IntToStr(FProjects.Count));
FProjects.Add('10'); //Гарантирует, что FProjects будет с Count>0
end;
end;
...
FProjects:=Classes.TStringList.Create; // Здесь FProjects пуст
end;
While FProjects[FProjects.Count-1]='' do FProjects.Delete(FProjects.Count-1); // А здесь вы вылетаете, т.к. пытаетесь в условии цикла while обратиться к строке FProjects.Count-1.
FProjects.Count у вас равен нулю, FProjects пуст, соответственно мы пытаемся прочитать строку номер 0 - 1, т.е. -1, причём из пустого списка => и получаем ошибку.
Если я правильно понял логику, то предлагаю заменить FProjects:=Classes.TStringList.Create на FProjects.Clear (иначе у вас будет утечка ресурсов) и потом поставить Exit, например, как-то так:
Procedure LoadFProjectsList;
var i:Integer;
begin
if FProjects.Count=0 then exit;
i:=Round(Form1.String_to_FloatNum(FProjects[0]));
If FProjects.Count>1 then FProjects.Delete(0) //If FProjects.Count>1 - на случай, если пока нет списка
else
begin
FProjects.Clear;
Exit;
end;
//Проверяем на пустые строки (часто в конце текстовых файлов остаются) для "поддержания чистоты", и сответствие FProjects её заданной длине; пустые строки в текстовых файлах не сохраняем
While FProjects[FProjects.Count-1]='' do FProjects.Delete(FProjects.Count-1);
While FProjects.Count>i do
FProjects.Delete(FProjects.Count-1);
While FProjects.Count<i do //если коротка, то доводим пустыми строками
FProjects.Add('');
end;
P.S. Хороший пример на тему, зачем нужно выкладывать код целиком, а не огрызки ;)
P.P.S. Дабл-клик по LoadFProjectsList в окне Call Stack привёл бы вас на строчку с while ;)
Procedure LoadFProjectsList;
var i:Integer;
begin
if FProjects.Count=0 then exit;
i:=Round(Form1.String_to_FloatNum(FProjects[0]));
If FProjects.Count>1 then FProjects.Delete(0) //If FProjects.Count>1 - на случай, если пока нет списка
else
begin
// ставил FreeAndNil(FProjects); ставил так же FProjects.Destroy; - не помогает
FProjects:=Classes.TStringList.Create;
end;
//Проверяем на пустые строки (часто в конце текстовых файлов остаются) для "поддержания чистоты", и сответствие FProjects её заданной длине; пустые строки в текстовых файлах не сохраняем
While FProjects[FProjects.Count-1]='' do FProjects.Delete(FProjects.Count-1);
While FProjects.Count>i do
FProjects.Delete(FProjects.Count-1);
While FProjects.Count<i do //если коротка, то доводим пустыми строками
FProjects.Add('');
end;
P.S. Обошёл я методом тыка это, просто смотря на "выполняется - не выполняется". В другом месте присвоил, т.п.
P.S.2 Проходился по словам Count по всему коду, не вылетел ли где за пределы %|.
20-10-2008 14:45 | Вопрос к автору: запрос дополнительной информации
1. Вы привели полный текст LoadFProjectsList или там есть ещё что-то? Что у вас там за многоточие?
2. На какую строчку встанет редактор кода, если дважды щёлкнуть по LoadFProjectsList в окне Call Stack?
>>> Главное, всё волшебным образом исполняется, если убрать одно простое выражение в следующей процедуре
Ничего удивительного - при этом у вас не будет выполняться проблемная функция LoadFProjectsList. У вас же стоит "if FProjects.Count=0 then exit;".
Главное, всё волшебным образом исполняется, если убрать одно простое выражение в следующей процедуре:
Procedure LoadFProjectsFile;
begin
FProjects:=Classes.Tstringlist.Create;
If FileExists(FProjects_file)=true then
FProjects.LoadFromFile(FProjects_file)
else
begin
Form1.Console_Message('Список предыдущих проектов пуст');
//FProjects.Add('zxcv'); //если его убрать, то всё ok. А так вылетает при любом добавлении string-типа
end;
end;
На вкладке "Compiler" опция "Stack frames" была включена, включение опции "Use Debug DCUs" сделал выполнение программы интересной :).
Вылетает в месте, приведённом на скриншоте: http://vleshka.narod.ru/tstrings2.jpg (110 кб).
Весь Form1.Create:
procedure TForm1.FormCreate(Sender: TObject);
begin
Form1.DoubleBuffered:=True; //чтоб не мерцала прога при сильной нагрузке на компьютер
//Загружаем список файлов недавно использовавшихся проектов
LoadFProjectsFile; //эта и следующая процедуры описаны здесь
LoadFProjectsList; //собственно, в этой процедуре и вылетает
FProjectsListMake; //составляет список недавно использовавшихся проектов в меню
Form1.Width:=470;
Form1.Height:=165;
Form1.Caption:='VLeshka Utility for MapInfo version '+version;
Console_Message('Добро пожаловать в систему VLeshka Utility for MapInfo.');
Form1.N6.Hint:='Обновляет настройки программы из файла';
Form1.OpenDialog1.InitialDir:=put_prog_vleshka;
Form1.HelpFile:=put_prog_vleshka+'VLESHKA MAPACADKAT.HLP';
if (VLeshka_screen_mode.X=0) or (VLeshka_screen_mode.Y=0) then
Form1.WindowState:=wsMaximized
else
begin
Form1.Width:=VLeshka_screen_mode.X;
Form1.Height:=VLeshka_screen_mode.Y;
end;
end;
>>> TForm1.FormCreate(???) [на нём и стоит выделение]
Ну-ка, покажите нам свой FormCreate. Кстати, если щёлкнуть по этой строке в окне Call stack, что покажет редактор кода?
Кстати, забыл сказать про одну вещь, извините.
Прежде чем использовать окно Call stack - зайдите в опции проекта и на вкладке "Compiler" включите опцию "Stack frames". И раз уж зашли - не помешает ещё включить опцию "Use Debug DCUs". После чего сделайте проекту Build (а не Compile или Run), запустите и попробуйте снова воспользоваться Call stack. Пощёлкайте по тем строчкам, что там будут.
20-10-2008 01:38 | Комментарий к предыдущим ответам
>>> Включите Range Checking и ищите ошибки.
Не поможет. Range Checking влияет только на данные, родственные массивам. Потому что для них у компилятора есть на руках информация о их размерах.
Например:
var
T: array[0..1] of Pointer;
I: Integer;
begin
I := 2;
T[I] := nil; // С Range Checking возбудит исключение. Компилятор может проверить, что I находиится в 0 <= I <= 1
Для TStringList нет никакого массива:
var
T: TStringList;
I: Integer;
begin
...
I := 2;
T[I] := ''; // Как компилятор узнает, в каком диапазоне должен лежать I? Никак.
Выход за диапазон в этом случае проверяет класс, т.к. у него уже на руках есть необходимая информация:
function TStringList.Get(Index: Integer): string;
begin
if (Index < 0) or (Index >= FCount) then Error(@SListIndexError, Index); // <- вот проверка
Result := FList^[Index].FString;
end;
Опция Range Checking в данном случае ни на что не влияет. Она могла бы влиять, если бы проверки в классе не было, т.к. в основе класса лежит массив. Но этого не происходит.
20-10-2008 01:25 | Вопрос к автору: запрос дополнительной информации
Вооооот.
А теперь, когда у вас открыто окно отладчика, идите в меню "View"/"Debug windows"/"Call stack".
И смотрим, что там написано. И ещё можно по строчкам в нём пощёлкать.
...
else
begin
// ставил FreeAndNil(FProjects); ставил так же FProjects.Destroy; - не помогает
FProjects:=Classes.TStringList.Create;
end;
//Проверяем на пустые строки (часто в конце текстовых файлов остаются) для "поддержания чистоты", и сответствие FProjects её заданной длине; пустые строки в текстовых файлах не сохраняем
While FProjects[FProjects.Count-1]='' do FProjects.Delete(FProjects.Count-1);
While FProjects.Count>i do
FProjects.Delete(FProjects.Count-1);
While FProjects.Count<i do //если коротка, то доводим пустыми строками
FProjects.Add('');
end;
Использую Delphi 7, просто выводит сообщение с кнопками "ОК" и "Help" (может в других предлагает Debug).
CPU Window показывает, что остановилась на строке "dec dword ptr [ebx+$10538bc7]".
Ссылка на скриншот http://vleshka.narod.ru/tStrings.jpg (100 кб).
>>> Выглядит так, что любой обращение к FProjects приводит к сообщению 'List index out of bounds (-1)'.
Вы под отладчиком запускали программу?
Когда отладчик показывает уведомление об исключении, вы нажимали на кнопку Ok/Debug? На какую строчку после этого встаёт отладчик? Что показывает окно Call Stack?
Что у вас в LoadFProjectsList в конце, там где многоточие? Нет обращения к строкам в FProjects?
Дубль три..
If FProjects.Count>1 then FProjects.Delete(0) //If FProjects.Count>1 - на случай, если пока нет списка
Жаль что здесь нельзя править свои сообщения...
//FProjects - глобальный.
var Form1:TForm1;
FProjects:tStrings;
Procedure LoadFProjectsFile;
begin
FProjects:=Classes.Tstringlist.Create;
If FileExists(FProjects_file)=true then
FProjects.LoadFromFile(FProjects_file)
else
begin
Form1.Console_Message('Список предыдущих проектов пуст');
//ShowMessage(IntToStr(FProjects.Count));
FProjects.Add('10');
end;
end;
Procedure LoadFProjectsList;
var i:Integer;
begin
if FProjects.Count=0 then exit;
i:=Round(Form1.String_to_FloatNum(FProjects[0]));
If FProjects.Count>0 then FProjects.Delete(0) //If FProjects.Count>1 - на случай, если пока нет списка
else
begin
// ставил FreeAndNil(FProjects); ставил так же FProjects.Destroy; - не помогает
FProjects:=Classes.TStringList.Create;
end;
|//...
end;
Выглядит так, что любой обращение к FProjects приводит к сообщению 'List index out of bounds (-1)'.
Привык использовать TStringList.Create и FreeAndNil перед ним, т.к. с другими методами очищения tStrings глюки были.
>>> FProjects.Clear тоже вылетает с ошибкой 'List index out of bounds (-1)
Расскажите, как вы определяете, в какой строке возникла ошибка.
>>> FProjects:=TStringList.Create; //этот самый действенный метод тоже не работает.. вылетает с сообщением Estring Error with Message 'List index out of bounds (-1)'
Если вы точно уверены, что исключение возбуждается именно в этой строке, то убедитесь, что вы используете именно стандартный класс TStringList. Попробуйте написать Classes.TStringList.Create. Дело в том, что стандартный дельфёвский TStringList не может возбуждать "List index out of bounds" в тех местах, что вы указали.
FProjects:=TStringList.Create; //этот самый действенный метод тоже не работает.. вылетает с сообщением Estring Error with Message 'List index out of bounds (-1)'
При этом в новой форме работает нрмально.
При работе в реальном приложении вылетает на этом ровном месте, что я описал.
В каких случаях это может происхоить ?
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.