当从TOleContainer中提取文档数据时,`DoVerb(ovInplaceActivate)`崩溃并出现各种错误消息

Ren*_*ann 5 delphi excel ole excel-2010

当通过OLE处理Office文档时,一位客户使用我们的软件会遇到一些奇怪的行为.当某个派生TOleContainer类的实例尝试通过DoVerb(ovInPlaceActivate)调用激活OLE对象时,代码崩溃.

有各种错误消息,包括:

  • (0x80030002) 找不到%1.
  • (0x80030005) 访问被拒绝.
  • (0x800706BE) 远程过程调用失败.

看我的代码:

function TfrmOleOffice.SaveToStream: TStream;
var
  LOleContainerState: TObjectState;
  LModified: Boolean;
begin
  Result := TMemoryStream.Create;
  if IsEmpty and OleOfficeAvailable then exit;

  if OleOfficeAvailable then
  begin
    LOleContainerStateBefore := FOleContainer.State;

    LModified := FOleContainer.Modified; // 'FOleContainer.Modified' could be changed by 'FOleContainer.Close'
    FOleContainer.Close;
    FValue.Position := 0;
    if LModified then // otherwise, take stored 'FValue' (see below)
      FOleContainer.SaveToStream(FValue);
    if LOleContainerStateBefore in [osUIActive] then
      ActivateContainer; // reactivate the container
  end;
  Result.CopyFrom(FValue, 0);
end;


//---------------------------------------------------


procedure THKSOleContainer.SaveToStream(Stream: TStream);
var
  TempLockBytes: ILockBytes;
  TempStorage: IStorage;
  DataHandle: HGlobal;
  Buffer: Pointer;
  Header: TStreamHeader;
  R: TRect;
  LFileName: String;
  LFileStream: TFileStream;
begin
  CheckObject;
  if FModSinceSave then SaveObject;

  // the following block might be obsolete
  if FCopyOnSave then
  begin
    OleCheck(CreateILockBytesOnHGlobal(0, True, TempLockBytes));
    OleCheck(StgCreateDocfileOnILockBytes(TempLockBytes, STGM_READWRITE
      or STGM_SHARE_EXCLUSIVE or STGM_CREATE, 0, TempStorage));
    OleCheck(FStorage.CopyTo(0, nil, nil, TempStorage));
    OleCheck(TempStorage.Commit(STGC_DEFAULT));
    OleCheck(GetHGlobalFromILockBytes(TempLockBytes, DataHandle));
  end else
    OleCheck(GetHGlobalFromILockBytes(FLockBytes, DataHandle));

  // save the document as a temporary file and read it into a TFileStream
  LFileName := IncludeTrailingPathDelimiter(GetMainTempFolder) + TPath.GetGUIDFileName + ExtractFileExt(FOriginalFileName); // get a unique temporary filename
  SaveOleObject(LFileName);
  try
    LFileStream := TFileStream.Create(LFileName, fmOpenRead or fmShareDenyNone);
    try
      Stream.CopyFrom(LFileStream, 0);
    finally
      FreeAndNil(LFileStream);
    end;
  finally
    SysUtils.DeleteFile(LFileName);
  end;

  FModified := False;
end;

procedure THKSOleContainer.SaveOleObject(AFileName: String = '');
var
  LActivatedBefore: Boolean;
begin
  LActivatedBefore := GetIsActivated;
  DoVerb(ovInPlaceActivate, False); // <-- this call crashes with various error messages on several systems of a client
  ForceDirectories(ExtractFilePath(AFileName));

  OleObject.SaveAs(AFileName);

  if not LActivatedBefore then
    Close(OLECLOSE_NOSAVE, False);
end;
Run Code Online (Sandbox Code Playgroud)

应该做的代码是什么?THKSOleContainer重新实现,SaveToStream但不是保存一些只有OLE容器可以正常打开的内部OLE流,而是将容器的内容保存到一些临时文件中并将其读回到TFileStream.结果应该是作为流的本机文档. TfrmOleOfficeTHKSOleContainer显示实例的表单.

什么情况下运行正常,什么不运行?首先,在我的电脑上,一切运行正常.我不知道任何其他客户也没有遇到这个问题.但是在该客户的计算机上,当文档被编辑并SaveToStream在表单确认时调用时,它会崩溃.如果该文件已加载,但没有被激活,它并没有崩溃.实际上,也SaveToStream被称为,但它成功了.

我使用Microsoft Excel 2010 - 家庭和商业,而客户安装了Microsoft Excel 2010 - Professional Plus.我的系统和他的系统是Windows 7 x64.

关于什么可能出错的任何想法?


"常问问题"

GSerg: 他们有防病毒而你没有?

  • 我们通过暂时停用防病毒检查,但没有效果.我们甚至重新启动了计算机(并确保防病毒软件仍然处于停用状态).

Ren*_*ann 2

FOleContainer.Close似乎是,函数中的调用TfrmOleOffice.SaveToStream导致了保存问题。在我提供了注释掉此调用的版本后,客户不再出现保存错误。

我们还发现了另一个麻烦制造者: Microsoft Dynamics NAV的 Excel COM 插件。停用后,加载时也不再出现错误。Excel 有时会在通话时冻结OleCreateFromFile。我们现在将尝试更改其加载行为,以便仅在需要时才加载它。这可能也导致了一些储蓄问题。