当从TOleContainer中提取文档数据时,使用DoVerb(ovInplaceActivate)会出现各种错误消息

5

一位客户在使用我们的软件时,通过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;

代码的目的是什么?THKSOleContainer重新实现了SaveToStream方法,但不是保存一些只有OLE容器可以正确打开的内部OLE流,而是将容器内容保存到某个临时文件中,并将其读回到TFileStream中。结果应该是本机文档作为流。

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

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

有什么想法可能出了什么问题吗?


"常见问题解答"

GSerg: 他们有杀毒软件而你没有吗?

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

他们有杀毒软件而你没有? - GSerg
@GSerg非常感谢您的回复。一个过于积极的杀毒软件是我们的第一个猜测。因此,我们暂时将其停用。不幸的是,问题仍然存在。 - René Hoffmann
1个回答

2
似乎在函数TfrmOleOffice.SaveToStream中调用FOleContainer.Close引起了保存问题。在我提供了一个版本并注释掉这个调用后,客户在保存时不再出现错误。
我们还发现了另一个麻烦制造者:一个针对Microsoft Dynamics NAV的Excel COM插件。在禁用该插件后,加载时不再出现错误。有时Excel在OleCreateFromFile调用时会冻结。我们现在将尝试更改其加载行为,使它仅在需要时加载。这也可能导致了一些保存问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接