EOleException:在通过Office.Interop.Word.Documents进行迭代时,被呼叫者拒绝了呼叫 [英] `EOleException: Call was rejected by callee` while iterating through `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屋!