Обход
rowCnt := UsedRange.Rows.Count;
colCnt := UsedRange.Columns.Count;
for Row := 1 to rowCnt do begin
for Col := 1 to colCnt do begin
v := UsedRange.Item[Row, Col].Value;
end;
end;
Это также работает (и может помочь вам найти обходной путь в более сложных случаях):
function ColCount(const range: ExcelRange): integer;
begin
Result := range.Columns.Count;
end;
for Row := 1 to UsedRange.Rows.Count do begin
for Col := 1 to ColCount(UsedRange) do begin
v := UsedRange.Item[Row, Col].Value;
end;
end;
Анализ
Сбой в System.Win.ComObj в DispCallByID при выполнении _Release в
varDispatch, varUnknown:
begin
if PPointer(Result)^ <> nil then
IDispatch(Result)._Release;
PPointer(Result)^ := Res.VDispatch;
end;
Хотя версия PUREPASCAL этой же процедуры в Delphi XE (XE использует версию на ассемблере) отличается ...
varDispatch, varUnknown:
begin
if PPointer(Result)^ <> nil then
IDispatch(Result.VDispatch)._Release;
PPointer(Result)^ := Res.VDispatch;
end;
... код ассемблера в обоих случаях одинаков (РЕДАКТИРОВАТЬ: не соответствует действительности, см. Мои заметки в конце):
@ResDispatch:
@ResUnknown:
MOV EAX,[EBX]
TEST EAX,EAX
JE @@2
PUSH EAX
MOV EAX,[EAX]
CALL [EAX].Pointer[8]
@@2: MOV EAX,[ESP+8]
MOV [EBX],EAX
JMP @ResDone
Интересно, что это вылетает ...
for Row := 1 to UsedRange.Rows.Count do begin
for Col := 1 to UsedRange.Columns.Count do begin
end;
end;
... а это не так.
row := UsedRange.Rows.Count;
col := UsedRange.Columns.Count;
col := UsedRange.Columns.Count;
Причиной этого является использование скрытых локальных переменных. В первом примере код компилируется в ...
00564511 6874465600 push $00564674
00564516 6884465600 push $00564684
0056451B A12CF35600 mov eax,[$0056f32c]
00564520 50 push eax
00564521 8D8508FFFFFF lea eax,[ebp-$000000f8]
00564527 50 push eax
00564528 E8933EEAFF call DispCallByIDProc
... и это называется дважды.
Во втором примере используются два разных временных расположения в стеке (смещения ebp - ????):
00564466 6874465600 push $00564674
0056446B 6884465600 push $00564684
00564470 A12CF35600 mov eax,[$0056f32c]
00564475 50 push eax
00564476 8D8514FFFFFF lea eax,[ebp-$000000ec]
0056447C 50 push eax
0056447D E83E3FEAFF call DispCallByIDProc
...
0056449B 6874465600 push $00564674
005644A0 6884465600 push $00564684
005644A5 A12CF35600 mov eax,[$0056f32c]
005644AA 50 push eax
005644AB 8D8510FFFFFF lea eax,[ebp-$000000f0]
005644B1 50 push eax
005644B2 E8093FEAFF call DispCallByIDProc
Ошибка возникает, когда очищается внутренний интерфейс, сохраненный в этом временном местоположении, что происходит только тогда, когда случай «для» выполняется во второй раз, потому что в этом интерфейсе уже что-то есть - он был помещен туда, когда «для» был вызван впервые. Во втором примере используются два расположения, поэтому этот внутренний интерфейс всегда инициализируется равным 0, а Release вообще не вызывается.
Истинная ошибка в том, что этот внутренний интерфейс содержит мусор, и когда вызывается Release, происходит sh! T.
После еще нескольких копаний я заметил, что ассемблерный код, который освобождает старый интерфейс, не тот же - в версии XE2 отсутствует одна инструкция "mov eax, [eax]". IOW
IDispatch(Result)._Release;
это ошибка, и она действительно должна быть
IDispatch(Result.VDispatch)._Release;
Неприятная ошибка RTL.