2016-05-17 127 views
0

我一直在Word2010.pas過去一週的工作,一切都很順利,直到我發現如果你打開一個手動文件,編輯它(但不保存),按Alt + F4,一個提示將顯示說如果你想保存你的文件或不,保持這樣的。 進入代碼並嘗試訪問該文檔,所有調用將導致EOleException: Call was rejected by callee。一旦你取消Word保存提示,一切工作正常。`EOleException:呼叫被拒絕被調用者`同時迭代'Office.Interop.Word.Documents`

我在編寫定期檢查文檔是否打開的代碼時遇到了這個問題。下面是一個檢查,如果該文件是開放的功能:(功能運行在一個計時器每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; 

有沒有人有這方面的經驗?有沒有我不知道的某種鎖?

更新#1:到目前爲止,我唯一能找到的解決方案是實現IOleMessageFilter,這樣一來我沒有收到任何異常,但程序停止,等待上線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; 

最好的解決方案FAR:我用IOleMessageFilter實現這樣的:(記住這將停止,等待就行了,我以前有一個例外)

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; 

回答

1

實際上,我認爲問題很簡單,Word忙於做模態對話框,因此無法響應外部COM調用。這個簡單的代碼產生了同樣的錯誤:

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

可能避免這個問題是,如果發生之前阻止它的最簡單方法。如果您使用Delphi附帶的TWordApplication服務器(在服務器組件選項卡上),您可以將事件處理程序附加到它的OnDocumentBeforeClose,並使用它來呈現您自己的「保存Y/N?」。對話框並將事件的Cancel param設置爲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對象讀取將導致「呼叫被拒絕......「的消息,所以我開始認爲這種行爲是有目的的 - 它告訴你該對象不處於可以與之交互的狀態。

有趣的是,它可以讀取雙捷Window對象,它會告訴你該文件的文件名,但不是文件的路徑的Caption財產。

實際上,我仍然認爲你最好的選擇是試着讓OnDocumentBeforeClose事件工作。我沒有Word 2007安裝在這臺機器上的Word 2007可以正常使用從Word2000.Pas派生的Word服務器對象,因此您可以嘗試使用這些對象而不是Word2010.Pas,以便查看。

另一種可能性是簡單地捕捉「呼叫被拒絕...」異常,也許返回「不可用」作爲文檔FullName,然後再試。

如果你不使用TWordApplication,不知道如何捕捉到OnDocumentBeforeClose您使用訪問Word中的方法,讓我知道你如何訪問它,我會看看我能挖出一些代碼來做到這一點。

我依稀記得有一種檢測Word忙於模態對話框的方法 - 我會看看我是否能夠在稍後看到該位置,如果您仍然需要它。不過,您的IOleMessageFilter看起來比我目前發現的任何東西都更有前途。

+0

我試過OnDocumentBeforeClose事件,但似乎無法啓動並運行。我寧願手動執行此檢查,所以如果您知道一種方法來檢查Word是否忙碌,那就太棒了! – Eric

+0

我會去的,但首先(我應該問早些時候)它是什麼**完全**,你試圖做的「呼叫被拒絕被調用者」阻止? – MartynA

+0

獲取'WordDocument'的'FullName','WordApp.Documents.Item(I).FullName'這是崩潰的地方。 – Eric