Я отслеживаю ошибку глубоко в стороннем коде, где оператор
fStringLen := Str - fToIdent;
выдает ошибку во время выполнения:
Str — это частный параметр функции PChar, на данный момент это ''nil {%0} в окне инспектора:На данный момент неясно, было ли что-нибудь. Отслеживая все происшествия, похоже, с
fToIndent Свойство PChar (у него нет установщика, на который я мог бы поставить точку останова), или if и это всего лишь начальное состояние после создания его объекта.
Существует несколько более старая 32-разрядная версия нашего программного обеспечения и более старая версия стороннего программного обеспечения, но этот фрагмент кода идентичен. Значения те же, если я запускаю код, но тогда ошибки проверки диапазона нет.
Кстати, это код, см. последний оператор:
function TdaSynHighlighterSQL.HashKey(Str: PChar): Integer;
// called with Str = 'absolute'
function GetOrd: Integer;
begin
case Str^ of
'_': Result := 1;
'a'..'z': Result := 2 + Ord(Str^) - Ord('a');
'A'..'Z': Result := 2 + Ord(Str^) - Ord('A');
'@':
if fDialect in [sqlMSSQL7, sqlMSSQL2K] then
Result := 24
else
Result := 0;
else Result := 0;
end;
end;
begin
Result := 0;
while IsIdentChar(Str^) do // Always true until Str=''
begin
Result := (2 * Result + GetOrd) and $FFFFFF;
inc(Str);
end;
Result := Result and $FF; // 255
fStringLen := Str - fToIdent;
end;
Как лучше это исправить?
Мне кажется странным, что этот код все еще не будет 64-битным доказательством.
Спросите себя, как можно поместить 64-битное значение в 32-битную переменную, и вы увидите проблему.





Проблема вызвана использованием цикла while. Во время выполнения вашего fStringLen := Str - fToIdent; он уже указывает на память, превышающую длину вашей строки, что затем вызывает ошибку проверки диапазона.
Рассмотрим этот простой пример цикла while, где в качестве управляющего значения используется простое целое число.
while I < 10 do
begin
Memo1.Lines.Add('Value of I in current loop cycle is ' +IntToStr(I));
Inc(I,1);
end;
Memo1.Lines.Add('Value of I after loop is: '+IntToStr(I));
Вы могли бы ожидать, что значение управляющей переменной I будет равно 9 после завершения цикла while, но это не так. На самом деле это 10. Почему так.
Это связано с тем, что весь цикл проверяет значение управляющей переменной перед началом каждого цикла. Но я увеличил значение управляющей переменной за пределы желаемого диапазона в последнем цикле цикла.
Поэтому вам может потребоваться вычислить fStringLen внутри цикла, прежде чем увеличивать значение Str.
while IsIdentChar(Str^) do // Always true until Str=''
begin
Result := (2 * Result + GetOrd) and $FFFFFF;
fStringLen := Str - fToIdent;
inc(Str);
end;
Однако это приведет к обновлению fStringLen для каждого символа в вашей строке.
Но если вы хотите избежать этого и по-прежнему вычислять fStringLen после цикла, вам нужно уменьшить значение Str на единицу раньше, чтобы оно отображало позицию последнего символа в вашей строке.
PS: Я не понимаю, почему ваш код вызывает исключение диапазона только в 64-й сборке. Это также должно было вызвать исключение диапазона в 32-битной сборке.
Перемещение вычисления fStringLen перемещает только место ошибки ;-)
Комментарий Дэвида Хеффернана указал в правильном направлении:
fToIdent не инициализируется, поскольку функция IdentKind(), которая его устанавливает, еще не была вызвана (это не ошибка).
В 32-битной версии fStringLen становится 245556086, если fToIdent=nil и оно соответствует целому числу.
В 64-битной версии fStringLen становится 1513182692840, если fToIdent=nil и оно не помещается в целое число.
Мы сделали быстрый патч в исходниках:
// fStringLen := Str - fToIdent;
// Prevent rangecheck error
if (fToIdent=nil) then
fStringLen := 0
else
fStringLen := Str - fToIdent;
КСТАТИ. Это было в ReportBuilder 22.03, на форуме поддержки получил такой ответ:
Версия SynEdit, используемая в ReportBuilder, была сильно адаптирована и изначально не предназначалась для 64-битных приложений. Мы преобразовали его для этого, однако иногда возникают проблемы, которые ускользают от внимания. Теперь для RB 22.04 доступен патч, устраняющий эту проблему.
Мы еще не обновились до 22.04, поэтому я не знаю, как они это исправили.
если
fToIdentравно нулю, аStrуказывает на конец данной строки и эта строка выделена в верхней позиции памяти, то это может вызвать эту ошибку диапазона. ИМХО следуетfToIdentуказать на начало строки для расчета длины.