EOleException:在通过Office.Interop.Word.Documents进行迭代时,被呼叫者拒绝了呼叫 [英] `EOleException: Call was rejected by callee` while iterating through `Office.Interop.Word.Documents`

查看:151
本文介绍了EOleException:在通过Office.Interop.Word.Documents进行迭代时,被呼叫者拒绝了呼叫的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

过去一周我一直在与Word2010.pas一起工作,一切进展顺利,直到我发现如果您手动打开文档对其进行编辑(但不要保存),按Alt + F4 ,系统会显示一个提示,提示您是否要保存文档,就这样保存. 进入代码并尝试访问该文档,所有调用都将导致EOleException: Call was rejected by callee.取消该Word保存提示后,一切正常.

I've been working with Word2010.pas for the past week and everything went well, until I found out that if you open a document manually, edit it (but don't save), press Alt+F4, a prompt will show up saying if you want to save your document or not, leave it like that. Go into code and try to access that document, all calls will result in EOleException: Call was rejected by callee. Once you cancel that Word save prompt, everything works fine.

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

I came across this while writing code that periodically checks if a document is open. Here is the function that checks if the document is open: (function runs in a timer every 2 seconds)

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;

有人对此有任何经验吗?有我不知道的某种锁吗?

Does anyone have any experience with this? Is there some sort of a lock that I'm not aware of?

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

UPDATE #1: So far the only solution I could find was to implement IOleMessageFilter, this way I do not receive any exceptions but the program stops and waits on the line WordApp.Documents.Item(I).FullName, but that is not what I want. Implementation of IOleMessageFilter goes like this:

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实现:(请记住,它将停止并在我之前遇到异常的行上等待)

BEST SOLUTION SO FAR: I used IOleMessageFilter implementation like this: (remember this will stop and wait on the line where I previously got an exception)

function IsWordDocumentOpen(FileName: string): Boolean;
var
  OleMessageFilter: IOleMessageFilter;
  WordApp: TWordApplication;
  I: Integer;
begin
  Result := False;
  try
    OleMessageFilter := IOleMessageFilter.Create;
    OleMessageFilter.RegisterFilter;

    WordApp := TWordApplication.Create(nil);
    try
      WordApp.Connect;
      for I := 1 to WordApp.Documents.Count do
      begin
        if WordApp.Documents.Item(I).FullName = FileName then
        begin
          Result := True;
          System.Break;
        end;
      end;
    finally
      OleMessageFilter.RevokeFilter;
      FreeAndNil(WordApp);
      FreeAndNil(OleMessageFilter);
    end;
  finally
    //
  end;
end;

推荐答案

实际上,我认为问题仅在于Word忙于执行模态对话框,因此无法响应外部COM调用.这个简单的代码会产生相同的错误:

Actually, I think that the problem is simply that Word is busy doing a modal dialog and so can't respond to external COM calls. This trivial code produces the same error:

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

避免此问题的最简单方法可能是在发生这种情况之前先解决它.如果您使用的是Delphi随附的TWordApplication服务器(位于服务器组件"选项卡上),则可以将事件处理程序附加到其OnDocumentBeforeClose上,并使用该事件处理程序来呈现自己的保存是/否?".对话框,并将事件的Cancel参数设置为True,以防止出现Word的对话框.

Probably the simplest way to avoid this problem is to head it off before if happens. If you are using the TWordApplication server that comes with Delphi (on the Servers components tab), you can attach an event handler to its OnDocumentBeforeClose and use that to present your own "Save Y/N?" dialog and set the event's Cancel param to True to prevent Word's dialog from appearing.

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

Update: If you try experimenting with this code while the Save dialog is popped up

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对象读取的尝试都会导致呼叫被拒绝..."消息,因此,我开始认为这种行为是设计使然-它告诉您对象处于无法与之交互的状态.

I think you'll find that any attempt to read from the vDoc object will result in the "Call was rejected ..." message, so I am beginning to think that this behaviour is by design - it's telling you that the object is not in a state that it can be interacted with.

有趣的是,有可能 读取vWin Window对象的Caption属性,该属性将告诉您文件的文件名而不是文件的路径.

Interestingly, it is possible to read the Caption property of the vWin Window object, which will tell you the filename of the file but not the file's path.

实际上,我仍然认为您最好的选择是尝试使OnDocumentBeforeClose事件正常运行.我没有在Word 2007的这台计算机上安装Word 2010,但可以与从Word2000.Pas派生的Word服务器对象一起正常工作,因此您可以尝试使用这些对象而不是Word2010.Pas.

Realistically, I still think your best option is to try and get the OnDocumentBeforeClose event working. I don't have Word 2010 installed on this machine by Word 2007 works fine with the Word server objects derived from Word2000.Pas so you might try those instead of Word2010.Pas, just to see.

另一种可能性就是简单地捕获呼叫被拒绝..."异常,也许返回"Unavailable"作为文档FullName,然后重试.

Another possibility is simply to catch the "Call was rejected ..." exception, maybe return "Unavailable" as the document FullName, and try again later.

如果您不使用TWordApplication,并且不知道如何捕获用于访问Word的方法的OnDocumentBeforeClose,请让我知道您正在如何访问它并我会看看我是否可以挖掘出一些代码来做到这一点.

If you're not using TWordApplication and don't know how to catch the OnDocumentBeforeClose for the method your using to access Word, let me know how you are accessing it and I'll see if I can dig out some code to do it.

我隐约记得有一种方法可以检测到Word是否正在使用模式对话框-我将在以后发现是否仍然需要的地方找到我.不过,您的IOleMessageFilter看起来比我迄今为止发现的任何东西都更有希望.

I vaguely recall there's a way of detecting that Word is busy with a modal dialog - I'll see if I can find where I saw that a bit later if you still need it. Your IOleMessageFilter looks more promising than anything I've found as yet, though.

这篇关于EOleException:在通过Office.Interop.Word.Documents进行迭代时,被呼叫者拒绝了呼叫的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆