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

`DoVerb(ovInplaceActivate)` crashes with various error messages when a document's data is extracted from TOleContainer

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

有各种错误信息,包括:

查看我的代码:

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;

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

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

我使用 Microsoft Excel 2010 - Home and Business,而客户使用 Microsoft Excel 2010 - Professional Plus 已安装。我的系统和他的系统都是Windows 7 x64.

有什么可能出错的想法吗?


"FAQ"

GSerg: They have an antivirus and you don't?

似乎是,在函数 TfrmOleOffice.SaveToStream 中调用 FOleContainer.Close 导致了保存问题。在我提供 此调用被注释掉.

的版本后,客户在保存时不再出现错误

我们还检测到 另一个麻烦制造者:Excel COM 加载项用于 Microsoft Dynamics NAV。停用后,加载时不再发生错误,也没有。 Excel 有时在 OleCreateFromFile 通话时会呆在那里。我们现在将尝试更改其加载行为,以便仅在需要时加载它。可能这也导致了一些保存方面的问题。