delphi – XE2中的COM是否断开,我该如何解决它?

前端之家收集整理的这篇文章主要介绍了delphi – XE2中的COM是否断开,我该如何解决它?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
更新:XE2 Update 2修复了下面描述的错误

下面的程序,从实际程序中减少,失败,在XE2中有一个例外。这是一个从2010年的回归。我没有XE测试,但我希望该程序在XE上运行良好(感谢Primož确认代码在XE上运行良好)。

program COMbug;

{$APPTYPE CONSOLE}

uses
  SysUtils,Variants,Windows,Excel2000;

var
  Excel: TExcelApplication;
  Book: ExcelWorkbook;
  Sheet: ExcelWorksheet;
  UsedRange: ExcelRange;
  Row,Col: Integer;
  v: Variant;

begin
  Excel := TExcelApplication.Create(nil);
  try
    Excel.Visible[LOCALE_USER_DEFAULT] := True;
    Book := Excel.Workbooks.Add(EmptyParam,LOCALE_USER_DEFAULT) as ExcelWorkbook;
    Sheet := Book.Worksheets.Add(EmptyParam,EmptyParam,1,LOCALE_USER_DEFAULT) as ExcelWorksheet;

    Sheet.Cells.Item[1,1].Value := 1.0;
    Sheet.Cells.Item[2,2].Value := 1.0;
    UsedRange := Sheet.UsedRange[LOCALE_USER_DEFAULT] as ExcelRange;
    for Row := 1 to UsedRange.Rows.Count do begin
      for Col := 1 to UsedRange.Columns.Count do begin
        v := UsedRange.Item[Row,Col].Value;
      end;
    end;
  finally
    Excel.Free;
  end;
end.

在XE2 32位的错误是:

Project COMbug.exe raised exception class $C000001D with message ‘system exception (code 0xc000001d) at 0x00dd6f3e’.

在UsedRange.Columns的第二次执行时发生错误

在XE2 64位的错误是:

Project COMbug.exe raised exception class $C0000005 with message ‘c0000005 ACCESS_VIOLATION’

再次,我认为错误发生在UsedRange.Columns的第二次执行,但是64位调试程序以一种稍微奇怪的方式逐步通过代码,所以我不是100%肯定的。

我已经提交了一个QC report的问题。

我非常喜欢我,好像在Delphi COM /自动化/接口栈中的东西被全面打破。这是我XE2采用的一个完整的显示

有没有人有这个问题的任何经验?有没有人有任何提示和建议,如何我可以尝试解决这个问题?调试这里真正发生的是我的专业领域之外。

解决方法

解决方法
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;

分析

执行_Release时,它在DispCallByID中的System.Win.ComObj中崩溃

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 – ???? offsets):

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

当存储在该临时位置的内部接口被清除时,该错误发生,这仅在第二次执行“for”情况时发生,因为在该接口中已经存储了一些东西 – 当“for”被调用时它被放在那里首次。在第二个示例中,使用两个位置,因此此内部接口始终初始化为0,并且根本不调用释放。

真正的错误是,这个内部接口包含垃圾,当Release被调用时,sh!t发生。

在一些更多的挖掘之后,我注意到释放旧接口的汇编代码是不一样的 – XE2版本缺少一个“mov eax,[eax]”指令。 IOW,

IDispatch(Result)._Release;

是一个错误,它真的应该是

IDispatch(Result.VDispatch)._Release;

讨厌的RTL错误

猜你在找的Delphi相关文章