过去一周我一直与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
答案 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看起来比我发现的任何东西都更有希望。