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" - это то, что тебе нужно.


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.

Другие вопросы по тегам