AVE: нарушение прав доступа @ адрес в модуле: DELPHI7
Это обработанная процедура замены 81 TEdit-s (MyEdit[bx,by,x,y]), динамически созданная и сгруппированная в 4-мерном массиве. Сейчас я пытаюсь запрограммировать изменения, чтобы они принимали только 1 цифру. Часть "только цифры" работает нормально, но часть "только 1 цифра" выдает ошибку нарушения доступа (AVE). Код:
procedure TForm1.OnHandleChange(Sender: TObject);
var
bx,by,x,y,len : Integer;
begin
bx:=(sender as TEdit).tag div 1000;
by:=(sender as TEdit).tag div 100-10*bx;
x:=(sender as TEdit).tag div 10-100*bx-10*by;
y:=(sender as TEdit).tag-bx*1000-by*100-x*10;
{*The line below gives the error*}
if not (MyEdit[bx,by,x,y].text[1] in ['1'..'9']) then MyEdit[bx,by,x,y].text:='';
MyEdit[bx,by,x,y].SelStart:=length(MyEdit[bx, by, x,y].text);
if length(MyEdit[bx, by, x,y].text) >1
then MyEdit[bx,by,x,y].text:=MyEdit[bx,by,x,y].text[2];
end;
Программа выполняется и работает, хотя есть AVE. Каждый раз, когда я ввожу букву вместо цифры - появляется ошибка, я нажимаю ОК, и программа выполняет свою работу. Но необходимо устранить эту ошибку. Там в любом случае?
2 ответа
MyEdit[bx,by,x,y].text[1]
делает предположение, что текст поля ввода не является пустым, то есть имеет по крайней мере 1 отдельный символ. Когда текст в поле ввода пуст, Text
Свойство является пустой строкой, и доступ к первому символу приводит к нарушению прав доступа.
Я отмечаю, что вы явно устанавливаете текст в поле для редактирования в пустую строку, и, конечно, пользователь может это сделать. Так что вам, безусловно, нужно следить за этой возможностью.
Решите проблему, проверив, является ли поле редактирования пустым.
var
Text: string;
....
Text := MyEdit[bx,by,x,y].Text;
if (Length(Text)=1) and (Text[1] in ['1'..'9']) then
....
Другой возможный кандидат на нарушение прав доступа, если MyEdit[bx,by,x,y]
приводит к выходу за пределы массива. Возможно, ваша математика с полем Tag все испортила. Это выглядит довольно странно для меня.
Как я утверждаю ниже, Sender as TEdit
казалось бы, имеет больше смысла здесь.
Более общие комментарии:
- Вы действительно должны отделить представление, то есть GUI от основных данных. Вы действительно не хотите решать с массивом визуальных элементов управления 4D в качестве входных данных.
- Не повторяй
(Sender as TEdit).tag
, Прочитайте это значение один раз в локальную переменную. Или, возможно, магазин(Sender as TEdit)
в локальную переменную. - Почему вы рассчитываете
bx
,by
,x
а такжеy
совсем? конечноSender as TEdit
это все, что тебе нужно. - Даже если вам нужно их вычислить, не повторяйте себя, написав
MyEdit[bx,by,x,y]
больше чем единожды. Сохраните эту ссылку в локальной переменной и используйте ее в любой последующей ссылке. - Если вам нужно рассчитать
bx
,by
,x
а такжеy
отTag
, не делайте это встроенным в обработчик событий. Поместите этот расчет в специальный вспомогательный метод. И также выделенный вспомогательный метод, который идет в противоположном направлении. И проверьте, что эти функции действительно противоположны друг другу.
Чтобы дать вам пример, эти помощники могут выглядеть так:
procedure PackCoordinates(const bx, by, x, y: Byte; out Tag: Integer);
begin
LongRec(Tag).Bytes[0] := bx;
LongRec(Tag).Bytes[1] := by;
LongRec(Tag).Bytes[2] := x;
LongRec(Tag).Bytes[3] := y;
end;
procedure UnpackCoordinates(const Tag: Integer; out bx, by, x, y: Byte);
begin
bx := LongRec(Tag).Bytes[0];
by := LongRec(Tag).Bytes[1];
x := LongRec(Tag).Bytes[2];
y := LongRec(Tag).Bytes[3];
end;
Зачем вообще нужен этот обработчик? Просто установите маску TMaskEdit
, "9" - это то, что тебе нужно.
- http://docwiki.embarcadero.com/Libraries/XE3/en/System.MaskUtils.TEditMask
- http://docwiki.embarcadero.com/Libraries/XE3/en/Vcl.Mask.TCustomMaskEdit.EditMask
AV причина была обнаружена Дэвидом. Но по предложению TLama я привожу свои комментарии к ответу.
Существует также принцип СУХОЙ. Не делайте расчеты снова и снова. Сделайте это один раз и запомните результат.
- Потому что это быстрее, иногда намного быстрее (запрос TEdit.text - запрос службы Windows GDI, а не просто чтение обычной переменной).
- Потому что это более надежно - копируя один и тот же форум тут и там, вы можете сделать опечатку и закончить е [различными формулами.
- Потому что это более перспективно на будущее. Если бы вы когда-нибудь изменили формулу - вам нужно было бы сделать это только в одном месте, а не искать все его случаи.
- Как указал Дэвид,
Sender
уже ваше редактирование. Нет смысла искать его снова!
Не умножайте и не удаляйте - эти операции стоят дорого. Особенно, когда вы удаляете на 10 вместо 2,4,8,16,...
И просто не делите в любом случае - вы можете естественным образом разделить значения с помощью Typecast здесь.
Использование 4D массива очень странно. Очень. Вам это действительно нужно?
В целом все сводится к
type TTagSplit = packed record
case byte of
0: (Tag: integer);
1: (bx, by, x, y: byte); end;
procedure CreatingEdits...
var tt: TTagSplit; e: TEdit;
begin
for ... do begin
e := TEdit.Create(MainForm);
....
tt.bx := ...; tt. by := ....
e.Tag := tt.Tag;
end;
end;
procedure TForm1.OnHandleChange(Sender: TObject);
var
bx,by,x,y,len : Integer;
e : TEdit; txt: string;
tt: TTagSplit;
begin
e := Sender as TEdit;
tt.Tag := e.Tag;
bx := tt.bx; by := ....
// Here you do not need those bx and rest - just demo how to get them
txt := e.Text;
if length(txt) > 1
then e.Text := txt[2]
else
if txt > '' then
if not (txt[1] in ['1'..'9'])
then e.Text := '';
end;
Но если взять резюме - простое использование TMaskEdit сделает весь этот код ненужным. TMaskedit сам проверит, чтобы содержимое было однозначным или пустым. Единственное, что нужно проверить, это то, что цифра не равна нулю.
И вишня, чтобы возглавить этот пирог - вам вообще не нужно 81 поле для редактирования! Вы должны просто поставить один TStringGrid
установить размер 9x9.