Антон Григорьев дата публикации 16-08-2004 13:36 КАТЕГОРИЯ | | БИБЛИОТЕКА.VCL.List index out of bounds при корректном значении индекса | ПРОДУКТ | | Delphi | ПЛАТФОРМА | | Windows |
Windows позволяет с каждой строкой списка ListBox’а или ComboBox’а связать либо число, либо указатель (точнее – некоторую четырёхбайтную величину, которую программа может трактовать как число, как указатель или как что-либо ещё). В VCL эта возможность используется для привязки к строкам списка объектов – четырёхбайтная величина по умолчанию трактуется как TObject. Доступ к этим объектам осуществляется через свойства TComboBox.Items.Objects[Index] и TListBox.Items.Objects[Index] .
Иногда всё-таки требуется привязывать к строкам не объекты, а числа. Для этого можно воспользоваться явным приведением типов, например:
ComboBox1.Items.Objects[I] := TObject(17);
I := Integer(ComboBox1.Items.Objects[I]);
Если таким образом связать со строкой значение –1, то при попытке получить это значение возникнет исключение EStringListError с комментарием "List index out of bounds". Рассмотрим следующий пример:
ComboBox1.Items.Clear;
ComboBox1.Items.AddObject(‘Text’,TObject(-1));
I := Integer(ComboBox1.Items.Object[0]); {*}
Исключение возникнет при попытке выполнить строку, отмеченную звёздочкой, хотя очевидно, что индекс в данном случае корректен.
Чтобы понять причину ошибки, необходимо рассмотреть, как осуществляется чтение значения, привязанного к строке, на уровне Windows API. Рассмотрим это на примере ComboBox’а. Для получения значения необходимо послать ComboBox’у сообщение CB_GetItemData. Результатом обработки этого сообщения будет значение, связанное с указанной строкой, или CB_Err, если при обработке сообщения возникнет ошибка. При этом документация не уточняет, какие именно ошибки могут в принципе возникнуть и как узнать, какая именно ошибка произошла.
Метод TComboBoxStrings.GetObject , через который читается значение свойства Objects, интерпретирует получение CB_Err однозначно: генерирует EStringListError с комментарием "List index out of bounds".
Проблема заключается в том, что численное значение константы CB_Err = –1 . Поэтому и в случае ошибки, и в случае, когда строке сопоставлено значение –1, CB_GetItemData вернёт одинаковый результат. И метод TComboBoxStrings.GetObject интерпретирует этот результат как ошибку. (А что ему ещё остаётся делать?)
Аналогичная проблема по тем же причинам возникает и при использовании ListBox’а. Проблема является прямым следствием документированных особенностей работы Windows и модели работы VCL, поэтому встречается во всех версиях Windows и во всех версиях Delphi. Эта же проблема возникает при попытки использования значения 4294967295, т.к. на двоичном уровне это число записывается той же комбинацией бит, что и –1.
При использовании свойства Objects по прямому назначению, т.е. для хранения объектов, эта проблема не может возникнуть, потому что $FFFFFFFF – это адрес самого старшего байта в четырёхгигабайтном виртуальном адресном пространстве программы. Эта область адресного пространства зарезервирована системой, и менеджер памяти Delphi не может выделить память для объекта в этой области.
Рекомендуемые способы решения проблемы:
- Пересмотреть алгоритм и отказаться от связывания значения –1 со строками.
- Напрямую посылать CB_GetItemData ComboBox’у, а попадание индекса в диапазон контролировать самостоятельно другими методами.
[TListBox] [TComboBox] [Ядро, структуры и механизмы Windows, использование API] [Отображение списков, сеток]
Обсуждение материала [ 19-08-2004 13:59 ] 2 сообщения |