`EOleException:在通过`Office.Interop.Word.Documents`

时间:2016-05-17 10:47:33

标签: delphi ms-word office-interop

过去一周我一直与Word2010.pas合作,一切顺利,直到我发现如果您手动打开文档编辑< / strong>(但不要保存),按Alt + F4 ,会出现提示,说明您是否要保存文档,请保留原样。 进入代码并尝试访问该文档,所有调用都将生成EOleException: Call was rejected by callee。取消Word保存提示后,一切正常。

我在编写定期检查文档是否打开的代码时遇到了这个问题。这是检查文档是否打开的函数:(函数每2秒在一个计时器中运行

function IsWordDocumentOpen(FileName: string): Boolean;
var
  WordApp: TWordApplication;
  I: Integer;
begin
  Result := False;
  try
    WordApp := TWordApplication.Create(nil);
    try          
      WordApp.Connect;
      for I := 1 to WordApp.Documents.Count do
      begin
        try
          if WordApp.Documents.Item(I).FullName = FileName then
          begin
            Result := True;
            System.Break;
          end;
        except
          on E: EOleException do
            // I always end up here while the document has the prompt
        end;
      end;
    finally
      FreeAndNil(WordApp);
    end;
  finally
    //
  end;
end;

有没有人有这方面的经验?是否有某种我不知道的锁?

UPDATE#1:到目前为止,我能找到的唯一解决方案是实现IOleMessageFilter,这样我就不会收到任何异常,但程序会停止并等待行{{1} 1}},但那不是我想要的。 WordApp.Documents.Item(I).FullName的实施如下:

IOleMessageFilter

最佳解决方案:我使用了type IOleMessageFilter = class(TInterfacedObject, IMessageFilter) public function HandleInComingCall(dwCallType: Longint; htaskCaller: HTask; dwTickCount: Longint; lpInterfaceInfo: PInterfaceInfo): Longint;stdcall; function RetryRejectedCall(htaskCallee: HTask; dwTickCount: Longint; dwRejectType: Longint): Longint;stdcall; function MessagePending(htaskCallee: HTask; dwTickCount: Longint; dwPendingType: Longint): Longint;stdcall; procedure RegisterFilter(); procedure RevokeFilter(); end; implementation function IOleMessageFilter.HandleInComingCall(dwCallType: Integer; htaskCaller: HTask; dwTickCount: Integer; lpInterfaceInfo: PInterfaceInfo): Longint; begin Result := 0; end; function IOleMessageFilter.MessagePending(htaskCallee: HTask; dwTickCount, dwPendingType: Integer): Longint; begin Result := 2 //PENDINGMSG_WAITDEFPROCESS end; procedure IOleMessageFilter.RegisterFilter; var OldFilter: IMessageFilter; NewFilter: IMessageFilter; begin OldFilter := nil; NewFilter := IOleMessageFilter.Create; CoRegisterMessageFilter(NewFilter,OldFilter); end; function IOleMessageFilter.RetryRejectedCall(htaskCallee: HTask; dwTickCount, dwRejectType: Integer): Longint; begin Result := -1; if dwRejectType = 2 then Result := 99; end; procedure IOleMessageFilter.RevokeFilter; var OldFilter: IMessageFilter; NewFilter: IMessageFilter; begin OldFilter := nil; NewFilter := nil; CoRegisterMessageFilter(NewFilter,OldFilter); end; end; 这样的实现:(记住这将停止并等待我之前获得异常的行)

IOleMessageFilter

1 个答案:

答案 0 :(得分:1)

实际上,我认为问题只是Word忙于模式对话,因此无法响应外部COM调用。这个简单的代码会产生同样的错误:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Caption := MSWord.ActiveDocument.Name;
end;

避免这个问题的最简单方法可能是在发生之前将其关闭。如果您使用的是Delphi附带的TWordApplication服务器(在“服务器组件”选项卡上),您可以将事件处理程序附加到其OnDocumentBeforeClose并使用它来显示您自己的“保存Y / N?”对话框并将事件的Cancel参数设置为True以防止出现Word对话框。

更新:如果您在弹出Save对话框时尝试尝试使用此代码

procedure TForm1.Button1Click(Sender: TObject);
var
  vWin,
  vDoc,
  vApp : OleVariant;
begin
  vWin := MSWord.ActiveWindow;
  Caption := vWin.Caption;
  vDoc := vWin.Document;

  vApp := vDoc.Application;  //  Attempt to read Word Document property

  Caption := vDoc.Path + '\';
  Caption := Caption + vDoc.Name;
end;

我想你会发现任何从vDoc对象中读取的尝试都会导致“Call was rejected ...”消息,所以我开始认为这种行为是设计的 - 它告诉你对象不处于可以与之交互的状态。

有趣的是, 可以读取vWin Window对象的Caption属性,它会告诉你文件的文件名,而不是文件的路径。

实际上,我仍然认为您最好的选择是尝试让OnDocumentBeforeClose事件发挥作用。我没有在Word 2007上安装Word 2010,可以使用Word2000.Pas派生的Word服务器对象,所以你可以试试那些而不是Word2010.Pas,只是为了看看。

另一种可能性就是捕获“调用被拒绝...”异常,可能会将“不可用”作为文档FullName返回,稍后再试。

如果你没有使用TWordApplication并且不知道如何捕获用于访问Word的方法的OnDocumentBeforeClose,请告诉我 访问的方式它,我会看看我是否可以挖出一些代码来完成它。

我模糊地回想起有一种方法可以检测到Word正忙于模态对话 - 如果你还需要它,我会看看能不能找到我看到的地方。不过,你的IOleMessageFilter看起来比我发现的任何东西都更有希望。