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

<p>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: (<em>function runs in a timer every 2 seconds</em>)</p>

<pre><code>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;
</code></pre>

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

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

<pre><code>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;
</code></pre>

<p><strong>BEST SOLUTION SO FAR:</strong> I used <code>IOleMessageFilter</code> implementation like this: <em>(remember this will stop and wait on the line where I previously got an exception)</em></p>

<pre><code>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;
</code></pre>